Compare commits

..

36 Commits

Author SHA1 Message Date
Anthony Stirling
eef1a88c22 Update build.gradle 2025-02-24 23:22:28 +00:00
Anthony Stirling
1457804803 Merge branch 'main' into nameChanges 2025-02-24 23:13:34 +00:00
Anthony Stirling
5a953992df add view pdf to nav and remove duplicate home on view 2025-02-24 23:12:58 +00:00
Dario Ghunney Ware
4c701b2e69 SSO Refactoring (#2818)
# Description of Changes

* Refactoring of SSO code around OAuth & SAML 2
* Enabling auto-login with SAML 2 via the new `SSOAutoLogin` property
* Correcting typos & general cleanup

---

## Checklist

### General

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [x] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

- [x] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [x] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-24 22:18:34 +00:00
Ludy
16295c7bb9 move compress.grayscale.label to section #compress (#3048)
# Description of Changes

Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)

---

## Checklist

### General

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-24 22:13:20 +00:00
Anthony Stirling
69da443096 dynamic port for UI from 8080 up (8081 etc) (#3042)
# Description of Changes

Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-24 10:19:43 +00:00
Anthony Stirling
71cdc640c0 dynamic port for UI from 8080 up (8081 etc) 2025-02-23 23:46:38 +00:00
Ludy
ee6fbdd61f Enhance AppUpdateService: Add @Configuration Annotation for Improved Spring Boot Integration (#3036)
# Description of Changes

Please provide a summary of the changes, including:

The AppUpdateService class now includes the `@Configuration` annotation
in addition to the existing `@Service` annotation.

This update ensures that the class is properly registered as a
configuration class within the Spring application context, thereby
improving bean management and integration.

---

## Checklist

### General

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-23 20:02:46 +00:00
stirlingbot[bot]
a5da7fc2c3 🌐 Sync Translations + Update README Progress Table (#3040)
### Description of Changes

This Pull Request was automatically generated to synchronize updates to
translation files and documentation. Below are the details of the
changes made:

#### **1. Synchronization of Translation Files**
- Updated translation files (`messages_*.properties`) to reflect changes
in the reference file `messages_en_GB.properties`.
- Ensured consistency and synchronization across all supported language
files.
- Highlighted any missing or incomplete translations.

#### **2. Update README.md**
- Generated the translation progress table in `README.md`.
- Added a summary of the current translation status for all supported
languages.
- Included up-to-date statistics on translation coverage.

#### **Why these changes are necessary**
- Keeps translation files aligned with the latest reference updates.
- Ensures the documentation reflects the current translation progress.

---

Auto-generated by [create-pull-request][1].

[1]: https://github.com/peter-evans/create-pull-request

---------

Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-02-23 19:59:28 +00:00
Ludy
1f92318df4 Login Language Dropdown: Remove Flag Icon (#3037)
# Description of Changes

Please provide a summary of the changes, including:

Removed the flag icon image element from the language dropdown button in
the login template (`login.html`).

Closes #(issue_number)

---

## Checklist

### General

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-23 19:58:54 +00:00
dependabot[bot]
9df5e2aca0 Bump github/codeql-action from 3.28.9 to 3.28.10 (#3035)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [github/codeql-action](https://github.com/github/codeql-action)
from 3.28.9 to 3.28.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/github/codeql-action/releases">github/codeql-action's
releases</a>.</em></p>
<blockquote>
<h2>v3.28.10</h2>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>3.28.10 - 21 Feb 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.20.5. <a
href="https://redirect.github.com/github/codeql-action/pull/2772">#2772</a></li>
<li>Address an issue where the CodeQL Bundle would occasionally fail to
decompress on macOS. <a
href="https://redirect.github.com/github/codeql-action/pull/2768">#2768</a></li>
</ul>
<p>See the full <a
href="https://github.com/github/codeql-action/blob/v3.28.10/CHANGELOG.md">CHANGELOG.md</a>
for more information.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/github/codeql-action/blob/main/CHANGELOG.md">github/codeql-action's
changelog</a>.</em></p>
<blockquote>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>[UNRELEASED]</h2>
<p>No user facing changes.</p>
<h2>3.28.10 - 21 Feb 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.20.5. <a
href="https://redirect.github.com/github/codeql-action/pull/2772">#2772</a></li>
<li>Address an issue where the CodeQL Bundle would occasionally fail to
decompress on macOS. <a
href="https://redirect.github.com/github/codeql-action/pull/2768">#2768</a></li>
</ul>
<h2>3.28.9 - 07 Feb 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.20.4. <a
href="https://redirect.github.com/github/codeql-action/pull/2753">#2753</a></li>
</ul>
<h2>3.28.8 - 29 Jan 2025</h2>
<ul>
<li>Enable support for Kotlin 2.1.10 when running with CodeQL CLI
v2.20.3. <a
href="https://redirect.github.com/github/codeql-action/pull/2744">#2744</a></li>
</ul>
<h2>3.28.7 - 29 Jan 2025</h2>
<p>No user facing changes.</p>
<h2>3.28.6 - 27 Jan 2025</h2>
<ul>
<li>Re-enable debug artifact upload for CLI versions 2.20.3 or greater.
<a
href="https://redirect.github.com/github/codeql-action/pull/2726">#2726</a></li>
</ul>
<h2>3.28.5 - 24 Jan 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.20.3. <a
href="https://redirect.github.com/github/codeql-action/pull/2717">#2717</a></li>
</ul>
<h2>3.28.4 - 23 Jan 2025</h2>
<p>No user facing changes.</p>
<h2>3.28.3 - 22 Jan 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.20.2. <a
href="https://redirect.github.com/github/codeql-action/pull/2707">#2707</a></li>
<li>Fix an issue downloading the CodeQL Bundle from a GitHub Enterprise
Server instance which occurred when the CodeQL Bundle had been synced to
the instance using the <a
href="https://github.com/github/codeql-action-sync-tool">CodeQL Action
sync tool</a> and the Actions runner did not have Zstandard installed.
<a
href="https://redirect.github.com/github/codeql-action/pull/2710">#2710</a></li>
<li>Uploading debug artifacts for CodeQL analysis is temporarily
disabled. <a
href="https://redirect.github.com/github/codeql-action/pull/2712">#2712</a></li>
</ul>
<h2>3.28.2 - 21 Jan 2025</h2>
<p>No user facing changes.</p>
<h2>3.28.1 - 10 Jan 2025</h2>
<ul>
<li>CodeQL Action v2 is now deprecated, and is no longer updated or
supported. For better performance, improved security, and new features,
upgrade to v3. For more information, see <a
href="https://github.blog/changelog/2025-01-10-code-scanning-codeql-action-v2-is-now-deprecated/">this
changelog post</a>. <a
href="https://redirect.github.com/github/codeql-action/pull/2677">#2677</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b56ba49b26"><code>b56ba49</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2778">#2778</a>
from github/update-v3.28.10-9856c48b1</li>
<li><a
href="60c9c77c33"><code>60c9c77</code></a>
Update changelog for v3.28.10</li>
<li><a
href="9856c48b1a"><code>9856c48</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2773">#2773</a>
from github/redsun82/rust</li>
<li><a
href="9572e09da4"><code>9572e09</code></a>
Rust: fix log string</li>
<li><a
href="1a529366ac"><code>1a52936</code></a>
Rust: special case default setup</li>
<li><a
href="cf7e90952b"><code>cf7e909</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2772">#2772</a>
from github/update-bundle/codeql-bundle-v2.20.5</li>
<li><a
href="b7006aab6d"><code>b7006aa</code></a>
Merge branch 'main' into update-bundle/codeql-bundle-v2.20.5</li>
<li><a
href="cfedae723e"><code>cfedae7</code></a>
Rust: throw configuration errors if requested and not correctly
enabled</li>
<li><a
href="3971ed2a74"><code>3971ed2</code></a>
Merge branch 'main' into redsun82/rust</li>
<li><a
href="d38c6e60df"><code>d38c6e6</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2775">#2775</a>
from github/angelapwen/bump-octokit</li>
<li>Additional commits viewable in <a
href="9e8d0789d4...b56ba49b26">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.28.9&new-version=3.28.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-23 19:55:21 +00:00
dependabot[bot]
6aad45fcec Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#3034)
Bumps
[actions/upload-artifact](https://github.com/actions/upload-artifact)
from 4.6.0 to 4.6.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/upload-artifact/releases">actions/upload-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v4.6.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Update to use artifact 2.2.2 package by <a
href="https://github.com/yacaovsnc"><code>@​yacaovsnc</code></a> in <a
href="https://redirect.github.com/actions/upload-artifact/pull/673">actions/upload-artifact#673</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/upload-artifact/compare/v4...v4.6.1">https://github.com/actions/upload-artifact/compare/v4...v4.6.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="4cec3d8aa0"><code>4cec3d8</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/upload-artifact/issues/673">#673</a>
from actions/yacaovsnc/artifact_2.2.2</li>
<li><a
href="e9fad966cc"><code>e9fad96</code></a>
license cache update for artifact</li>
<li><a
href="b26fd06e9d"><code>b26fd06</code></a>
Update to use artifact 2.2.2 package</li>
<li>See full diff in <a
href="65c4c4a1dd...4cec3d8aa0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/upload-artifact&package-manager=github_actions&previous-version=4.6.0&new-version=4.6.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-23 19:55:08 +00:00
dependabot[bot]
ec5018cc80 Bump ossf/scorecard-action from 2.4.0 to 2.4.1 (#3033)
Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action)
from 2.4.0 to 2.4.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ossf/scorecard-action/releases">ossf/scorecard-action's
releases</a>.</em></p>
<blockquote>
<h2>v2.4.1</h2>
<h2>What's Changed</h2>
<ul>
<li>This update bumps the Scorecard version to the v5.1.1 release. For a
complete list of changes, please refer to the <a
href="https://github.com/ossf/scorecard/releases/tag/v5.1.0">v5.1.0</a>
and <a
href="https://github.com/ossf/scorecard/releases/tag/v5.1.1">v5.1.1</a>
release notes.</li>
<li>Publishing results now uses half the API quota as before. The exact
savings depends on the repository in question.
<ul>
<li>use Scorecard library entrypoint instead of Cobra hooking by <a
href="https://github.com/spencerschrock"><code>@​spencerschrock</code></a>
in <a
href="https://redirect.github.com/ossf/scorecard-action/pull/1423">ossf/scorecard-action#1423</a></li>
</ul>
</li>
<li>Some errors were made into annotations to make them more visible
<ul>
<li>Make default branch error more prominent by <a
href="https://github.com/jsoref"><code>@​jsoref</code></a> in <a
href="https://redirect.github.com/ossf/scorecard-action/pull/1459">ossf/scorecard-action#1459</a></li>
</ul>
</li>
<li>There is now an optional <code>file_mode</code> input which controls
how repository files are fetched from GitHub. The default is
<code>archive</code>, but <code>git</code> produces the most accurate
results for repositories with <code>.gitattributes</code> files at the
cost of analysis speed.
<ul>
<li>add input for specifying <code>--file-mode</code> by <a
href="https://github.com/spencerschrock"><code>@​spencerschrock</code></a>
in <a
href="https://redirect.github.com/ossf/scorecard-action/pull/1509">ossf/scorecard-action#1509</a></li>
</ul>
</li>
<li>The underlying container for the action is now <a
href="https://github.com/ossf/scorecard-action/pkgs/container/scorecard-action">hosted
on GitHub Container Registry</a>. There should be no functional changes.
<ul>
<li>🌱 publish docker images to GitHub Container Registry by <a
href="https://github.com/spencerschrock"><code>@​spencerschrock</code></a>
in <a
href="https://redirect.github.com/ossf/scorecard-action/pull/1453">ossf/scorecard-action#1453</a></li>
</ul>
</li>
</ul>
<h3>Docs</h3>
<ul>
<li>Installation docs update by <a
href="https://github.com/JeremiahAHoward"><code>@​JeremiahAHoward</code></a>
in <a
href="https://redirect.github.com/ossf/scorecard-action/pull/1416">ossf/scorecard-action#1416</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/JeremiahAHoward"><code>@​JeremiahAHoward</code></a>
made their first contribution in <a
href="https://redirect.github.com/ossf/scorecard-action/pull/1416">ossf/scorecard-action#1416</a></li>
<li><a href="https://github.com/jsoref"><code>@​jsoref</code></a> made
their first contribution in <a
href="https://redirect.github.com/ossf/scorecard-action/pull/1459">ossf/scorecard-action#1459</a>
<strong>Full Changelog</strong>: <a
href="https://github.com/ossf/scorecard-action/compare/v2.4.0...v2.4.1">https://github.com/ossf/scorecard-action/compare/v2.4.0...v2.4.1</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f49aabe0b5"><code>f49aabe</code></a>
bump docker to ghcr v2.4.1 (<a
href="https://redirect.github.com/ossf/scorecard-action/issues/1478">#1478</a>)</li>
<li><a
href="30a595ba86"><code>30a595b</code></a>
🌱 Bump github.com/sigstore/cosign/v2 from 2.4.2 to 2.4.3 (<a
href="https://redirect.github.com/ossf/scorecard-action/issues/1515">#1515</a>)</li>
<li><a
href="69ae593b7a"><code>69ae593</code></a>
omit vcs info from build (<a
href="https://redirect.github.com/ossf/scorecard-action/issues/1514">#1514</a>)</li>
<li><a
href="6a62a1cbf2"><code>6a62a1c</code></a>
add input for specifying <code>--file-mode</code> (<a
href="https://redirect.github.com/ossf/scorecard-action/issues/1509">#1509</a>)</li>
<li><a
href="2722664778"><code>2722664</code></a>
🌱 Bump the github-actions group with 2 updates (<a
href="https://redirect.github.com/ossf/scorecard-action/issues/1510">#1510</a>)</li>
<li><a
href="ae0ef3171a"><code>ae0ef31</code></a>
🌱 Bump github.com/spf13/cobra from 1.8.1 to 1.9.1 (<a
href="https://redirect.github.com/ossf/scorecard-action/issues/1512">#1512</a>)</li>
<li><a
href="3676bbc290"><code>3676bbc</code></a>
🌱 Bump golang from 1.23.6 to 1.24.0 in the docker-images group
(<a
href="https://redirect.github.com/ossf/scorecard-action/issues/1513">#1513</a>)</li>
<li><a
href="ae7548a0ff"><code>ae7548a</code></a>
Limit codeQL push trigger to main branch (<a
href="https://redirect.github.com/ossf/scorecard-action/issues/1507">#1507</a>)</li>
<li><a
href="9165624e75"><code>9165624</code></a>
upgrade scorecard to v5.1.0 (<a
href="https://redirect.github.com/ossf/scorecard-action/issues/1508">#1508</a>)</li>
<li><a
href="620fd28d6b"><code>620fd28</code></a>
🌱 Bump the github-actions group with 2 updates (<a
href="https://redirect.github.com/ossf/scorecard-action/issues/1505">#1505</a>)</li>
<li>Additional commits viewable in <a
href="62b2cac7ed...f49aabe0b5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ossf/scorecard-action&package-manager=github_actions&previous-version=2.4.0&new-version=2.4.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-23 19:55:02 +00:00
ovargasp
2404ceb46e Updated spanish translations (#3039)
# Added some missing translation, updated some that were confusing in
spanish

Please provide a summary of the changes, including:

- What was changed: messages_es_ES.properties
- Why the change was made: add some missing translations and update some
others
- Any challenges encountered: no

Closes #(issue_number)

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-23 19:53:01 +00:00
Ludy
f23a2d508f Fix showupdate (#3032)
# Description of Changes

Please provide a summary of the changes, including:


![image](https://github.com/user-attachments/assets/1cfda2a0-32a3-4404-90cf-d0a4b77b65a7)

---

## Checklist

### General

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [x] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-23 16:19:30 +00:00
Anthony Stirling
f5ca02df1d Dynamic paths for tools and removal of unused book endpoints (#3018)
# Description of Changes

This pull request includes several changes primarily focused on
improving configuration management, removing deprecated methods, and
updating paths for external dependencies. The most important changes are
summarized below:

### Configuration Management Improvements:
* Added a new `RuntimePathConfig` class to manage dynamic paths for
operations and pipeline configurations
(`src/main/java/stirling/software/SPDF/config/RuntimePathConfig.java`).
* Removed the `bookAndHtmlFormatsInstalled` bean and its associated
logic from `AppConfig` and `EndpointConfiguration`
(`src/main/java/stirling/software/SPDF/config/AppConfig.java`,
`src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java`).
[[1]](diffhunk://#diff-4d774ec79aa55750c0a4739bee971b68877078b73654e863fd40ee924347e143L130-L138)
[[2]](diffhunk://#diff-750f31f6ecbd64b025567108a33775cad339e835a04360affff82a09410b697dL12-L35)
[[3]](diffhunk://#diff-750f31f6ecbd64b025567108a33775cad339e835a04360affff82a09410b697dL275-L280)

### External Dependency Path Updates:
* Updated paths for `weasyprint` and `unoconvert` in
`ExternalAppDepConfig` to use values from `RuntimePathConfig`
(`src/main/java/stirling/software/SPDF/config/ExternalAppDepConfig.java`).
[[1]](diffhunk://#diff-c47af298c07c2622aa98b038b78822c56bdb002de71081e102d344794e7832a6R12-L33)
[[2]](diffhunk://#diff-c47af298c07c2622aa98b038b78822c56bdb002de71081e102d344794e7832a6L104-R115)


### Minor Adjustments:
* Corrected a typo from "Unoconv" to "Unoconvert" in
`EndpointConfiguration`
(`src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java`).

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-23 13:36:21 +00:00
Ludy
1e29cf43fb Fix: Analytics Initialization Behavior (#3031)
# Description of Changes

Please provide a summary of the changes, including:

What was changed:

- Modified the default value of enableAnalytics in settings.yml.template
from `true` to `undefined`.

Why the change was made:

- The analytics setting was updated to prevent the value from defaulting
to true during initialization, which suppressed the display of the
prompt dialog. Changing it to `undefined` ensures that the user is
explicitly prompted to enable or disable analytics, thereby improving
user control.

Closes #(issue_number)

---

## Checklist

### General

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-23 12:28:15 +00:00
Ludy
ebfe00717d Fix: Ensure window.analyticsPromptBoolean is Defined for Analytics Dialog (#2958)
# Description of Changes

### What was changed
- Added a JavaScript block to correctly initialize
`window.analyticsPromptBoolean` with a Thymeleaf expression.
- Wrapped the script in `/*<![CDATA[*/` and `/*]]>*/` for compatibility
with XHTML.

### Why the change was made
- Previously, `window.analyticsPromptBoolean` was always `undefined`,
preventing the analytics dialog from being displayed correctly.
- This fix ensures that the variable is properly initialized, allowing
the dialog to appear when required.

### Challenges encountered
- Ensuring the Thymeleaf expression is correctly interpreted within the
JavaScript block.

---

## Checklist

### General
- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation
- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)
- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)
- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-23 00:52:35 +00:00
stirlingbot[bot]
063a7244a3 Update 3rd Party Licenses (#3028)
Auto-generated by StirlingBot

Signed-off-by: stirlingbot[bot] <1113334+stirlingbot[bot]@users.noreply.github.com>
Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-02-23 00:33:11 +00:00
Ludy
7b9e52dec6 Remove: pdfjs - only use pdfjs-legacy (#2864)
# Description of Changes

Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-22 23:50:20 +00:00
Abdur Rahman
296dbc5dea fix: ignore qpdf warnings (#2952) (#3002)
# Description of Changes

Please provide a summary of the changes, including:

- What was changed
  - Modified the qpdf command execution by adding the `--no-warn` flag
- Why the change was made
- The warning is misleading because the object is still present, so it
doesn't indicate a real error. Suppressing it with `--no-warn` ensures
the compression process completes successfully.

- Any challenges encountered

Closes #2952

---

## Checklist

### General

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-22 23:38:20 +00:00
dependabot[bot]
f6654a06d9 Bump springBootVersion from 3.4.2 to 3.4.3 (#3007)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps `springBootVersion` from 3.4.2 to 3.4.3.
Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.2 to
3.4.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-boot/releases">org.springframework.boot:spring-boot-starter-web's
releases</a>.</em></p>
<blockquote>
<h2>v3.4.3</h2>
<h2> New Features</h2>
<ul>
<li>Add TWENTY_FOUR to JavaVersion enum <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44209">#44209</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Console output may be lost when using Log4j2 with something that
replaces System.out <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44380">#44380</a></li>
<li>Maven plugin does not consistently use ArgFile for classpath
argument on Windows <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44328">#44328</a></li>
<li>Reactive Jetty web server does not fail fast when configured to use
a server name bundle which Jetty does not support <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44319">#44319</a></li>
<li>When web server application context refresh fails, the original
failure is lost if stopping or destroying the web server throws an
exception <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44317">#44317</a></li>
<li>View resolver for Thymeleaf should back off if spring-webmvc is not
present <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44296">#44296</a></li>
<li>WebServer is not destroyed when ReactiveWebServerApplicationContext
refresh fails <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44294">#44294</a></li>
<li>Non-default DataSource candidates are not considered in
H2ConsoleAutoConfiguration <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44293">#44293</a></li>
<li>Banner placeholder and defaults do not work during development <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44255">#44255</a></li>
<li>Mustache templates return with ISO-8859-1 charset rather than UTF-8
in Content-Type response header <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44193">#44193</a></li>
<li>Servlet EndpointRequest doesn't match web server namespace correctly
<a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44188">#44188</a></li>
<li>java.lang.ClassCastException when using default management security
with WebFlux and health probes enabled <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44052">#44052</a></li>
<li>Logback configuration that relies on inner-classes does not work in
a native image <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44025">#44025</a></li>
<li>IllegalStateException: Unable to register SSL bundle after 3.3.8 or
3.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43989">#43989</a></li>
<li>Metrics and health do not include non-default candidate beans <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43481">#43481</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Document that auto-configuration classes should be identified using
their binary names <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44303">#44303</a></li>
<li>Correct typo in MVC security when explaining when UserDetailsService
auto-configuration will back off <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44301">#44301</a></li>
<li>Link to JarLauncher's javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44170">#44170</a></li>
<li>When using observability annotations, recommend that care is taken
to avoid double instrumentation <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44145">#44145</a></li>
<li>Fix typo in Running Your Application <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44035">#44035</a></li>
<li>Document Kubernetes preStop handler when using a Docker image
without a shell <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44022">#44022</a></li>
<li>Source snippet in Developing Your First Spring Boot Application
section uses the root package <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43983">#43983</a></li>
<li>Correct the location of MyApplication.java in &quot;Developing Your
First Spring Boot Application&quot; <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43975">#43975</a></li>
<li>Add links to Jackson Javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43971">#43971</a></li>
<li>Warn that some Quartz database schema scripts must be modified
before use <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43958">#43958</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Upgrade to Commons Pool2 2.12.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44173">#44173</a></li>
<li>Upgrade to Couchbase Client 3.7.8 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44269">#44269</a></li>
<li>Upgrade to Groovy 4.0.25 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44174">#44174</a></li>
<li>Upgrade to Hibernate 6.6.8.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44332">#44332</a></li>
<li>Upgrade to HttpClient5 5.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44176">#44176</a></li>
<li>Upgrade to HttpCore5 5.3.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44177">#44177</a></li>
<li>Upgrade to Infinispan 15.0.13.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44178">#44178</a></li>
<li>Upgrade to jOOQ 3.19.19 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44368">#44368</a></li>
<li>Upgrade to Json-smart 2.5.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44264">#44264</a></li>
<li>Upgrade to Maven Clean Plugin 3.4.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44349">#44349</a></li>
<li>Upgrade to Micrometer 1.14.4 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44115">#44115</a></li>
<li>Upgrade to Micrometer Tracing 1.4.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44116">#44116</a></li>
<li>Upgrade to Native Build Tools Plugin 0.10.5 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44179">#44179</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2f53c0abc0"><code>2f53c0a</code></a>
Release v3.4.3</li>
<li><a
href="f99171fe2e"><code>f99171f</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="70e0744c27"><code>70e0744</code></a>
Next development version (v3.3.10-SNAPSHOT)</li>
<li><a
href="07d9db3b80"><code>07d9db3</code></a>
Merge pull request <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44380">#44380</a>
from nosan</li>
<li><a
href="22958097e3"><code>2295809</code></a>
Register Log42J StatusListener</li>
<li><a
href="575655ca39"><code>575655c</code></a>
Upgrade Tomcat 11 smoke tests to Tomcat 11.0.4</li>
<li><a
href="c74397af1a"><code>c74397a</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="c718461450"><code>c718461</code></a>
Protect against NoSuchMethodException on setReadOnly</li>
<li><a
href="7dc9bf29b8"><code>7dc9bf2</code></a>
Upgrade to Testcontainers Redis Module 2.2.4</li>
<li><a
href="7d1fc062bf"><code>7d1fc06</code></a>
Upgrade to Testcontainers 1.20.5</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `org.springframework.boot:spring-boot-starter-jetty` from 3.4.2
to 3.4.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-boot/releases">org.springframework.boot:spring-boot-starter-jetty's
releases</a>.</em></p>
<blockquote>
<h2>v3.4.3</h2>
<h2> New Features</h2>
<ul>
<li>Add TWENTY_FOUR to JavaVersion enum <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44209">#44209</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Console output may be lost when using Log4j2 with something that
replaces System.out <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44380">#44380</a></li>
<li>Maven plugin does not consistently use ArgFile for classpath
argument on Windows <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44328">#44328</a></li>
<li>Reactive Jetty web server does not fail fast when configured to use
a server name bundle which Jetty does not support <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44319">#44319</a></li>
<li>When web server application context refresh fails, the original
failure is lost if stopping or destroying the web server throws an
exception <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44317">#44317</a></li>
<li>View resolver for Thymeleaf should back off if spring-webmvc is not
present <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44296">#44296</a></li>
<li>WebServer is not destroyed when ReactiveWebServerApplicationContext
refresh fails <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44294">#44294</a></li>
<li>Non-default DataSource candidates are not considered in
H2ConsoleAutoConfiguration <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44293">#44293</a></li>
<li>Banner placeholder and defaults do not work during development <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44255">#44255</a></li>
<li>Mustache templates return with ISO-8859-1 charset rather than UTF-8
in Content-Type response header <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44193">#44193</a></li>
<li>Servlet EndpointRequest doesn't match web server namespace correctly
<a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44188">#44188</a></li>
<li>java.lang.ClassCastException when using default management security
with WebFlux and health probes enabled <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44052">#44052</a></li>
<li>Logback configuration that relies on inner-classes does not work in
a native image <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44025">#44025</a></li>
<li>IllegalStateException: Unable to register SSL bundle after 3.3.8 or
3.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43989">#43989</a></li>
<li>Metrics and health do not include non-default candidate beans <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43481">#43481</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Document that auto-configuration classes should be identified using
their binary names <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44303">#44303</a></li>
<li>Correct typo in MVC security when explaining when UserDetailsService
auto-configuration will back off <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44301">#44301</a></li>
<li>Link to JarLauncher's javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44170">#44170</a></li>
<li>When using observability annotations, recommend that care is taken
to avoid double instrumentation <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44145">#44145</a></li>
<li>Fix typo in Running Your Application <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44035">#44035</a></li>
<li>Document Kubernetes preStop handler when using a Docker image
without a shell <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44022">#44022</a></li>
<li>Source snippet in Developing Your First Spring Boot Application
section uses the root package <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43983">#43983</a></li>
<li>Correct the location of MyApplication.java in &quot;Developing Your
First Spring Boot Application&quot; <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43975">#43975</a></li>
<li>Add links to Jackson Javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43971">#43971</a></li>
<li>Warn that some Quartz database schema scripts must be modified
before use <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43958">#43958</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Upgrade to Commons Pool2 2.12.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44173">#44173</a></li>
<li>Upgrade to Couchbase Client 3.7.8 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44269">#44269</a></li>
<li>Upgrade to Groovy 4.0.25 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44174">#44174</a></li>
<li>Upgrade to Hibernate 6.6.8.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44332">#44332</a></li>
<li>Upgrade to HttpClient5 5.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44176">#44176</a></li>
<li>Upgrade to HttpCore5 5.3.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44177">#44177</a></li>
<li>Upgrade to Infinispan 15.0.13.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44178">#44178</a></li>
<li>Upgrade to jOOQ 3.19.19 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44368">#44368</a></li>
<li>Upgrade to Json-smart 2.5.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44264">#44264</a></li>
<li>Upgrade to Maven Clean Plugin 3.4.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44349">#44349</a></li>
<li>Upgrade to Micrometer 1.14.4 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44115">#44115</a></li>
<li>Upgrade to Micrometer Tracing 1.4.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44116">#44116</a></li>
<li>Upgrade to Native Build Tools Plugin 0.10.5 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44179">#44179</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2f53c0abc0"><code>2f53c0a</code></a>
Release v3.4.3</li>
<li><a
href="f99171fe2e"><code>f99171f</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="70e0744c27"><code>70e0744</code></a>
Next development version (v3.3.10-SNAPSHOT)</li>
<li><a
href="07d9db3b80"><code>07d9db3</code></a>
Merge pull request <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44380">#44380</a>
from nosan</li>
<li><a
href="22958097e3"><code>2295809</code></a>
Register Log42J StatusListener</li>
<li><a
href="575655ca39"><code>575655c</code></a>
Upgrade Tomcat 11 smoke tests to Tomcat 11.0.4</li>
<li><a
href="c74397af1a"><code>c74397a</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="c718461450"><code>c718461</code></a>
Protect against NoSuchMethodException on setReadOnly</li>
<li><a
href="7dc9bf29b8"><code>7dc9bf2</code></a>
Upgrade to Testcontainers Redis Module 2.2.4</li>
<li><a
href="7d1fc062bf"><code>7d1fc06</code></a>
Upgrade to Testcontainers 1.20.5</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from
3.4.2 to 3.4.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-boot/releases">org.springframework.boot:spring-boot-starter-thymeleaf's
releases</a>.</em></p>
<blockquote>
<h2>v3.4.3</h2>
<h2> New Features</h2>
<ul>
<li>Add TWENTY_FOUR to JavaVersion enum <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44209">#44209</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Console output may be lost when using Log4j2 with something that
replaces System.out <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44380">#44380</a></li>
<li>Maven plugin does not consistently use ArgFile for classpath
argument on Windows <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44328">#44328</a></li>
<li>Reactive Jetty web server does not fail fast when configured to use
a server name bundle which Jetty does not support <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44319">#44319</a></li>
<li>When web server application context refresh fails, the original
failure is lost if stopping or destroying the web server throws an
exception <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44317">#44317</a></li>
<li>View resolver for Thymeleaf should back off if spring-webmvc is not
present <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44296">#44296</a></li>
<li>WebServer is not destroyed when ReactiveWebServerApplicationContext
refresh fails <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44294">#44294</a></li>
<li>Non-default DataSource candidates are not considered in
H2ConsoleAutoConfiguration <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44293">#44293</a></li>
<li>Banner placeholder and defaults do not work during development <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44255">#44255</a></li>
<li>Mustache templates return with ISO-8859-1 charset rather than UTF-8
in Content-Type response header <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44193">#44193</a></li>
<li>Servlet EndpointRequest doesn't match web server namespace correctly
<a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44188">#44188</a></li>
<li>java.lang.ClassCastException when using default management security
with WebFlux and health probes enabled <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44052">#44052</a></li>
<li>Logback configuration that relies on inner-classes does not work in
a native image <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44025">#44025</a></li>
<li>IllegalStateException: Unable to register SSL bundle after 3.3.8 or
3.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43989">#43989</a></li>
<li>Metrics and health do not include non-default candidate beans <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43481">#43481</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Document that auto-configuration classes should be identified using
their binary names <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44303">#44303</a></li>
<li>Correct typo in MVC security when explaining when UserDetailsService
auto-configuration will back off <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44301">#44301</a></li>
<li>Link to JarLauncher's javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44170">#44170</a></li>
<li>When using observability annotations, recommend that care is taken
to avoid double instrumentation <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44145">#44145</a></li>
<li>Fix typo in Running Your Application <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44035">#44035</a></li>
<li>Document Kubernetes preStop handler when using a Docker image
without a shell <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44022">#44022</a></li>
<li>Source snippet in Developing Your First Spring Boot Application
section uses the root package <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43983">#43983</a></li>
<li>Correct the location of MyApplication.java in &quot;Developing Your
First Spring Boot Application&quot; <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43975">#43975</a></li>
<li>Add links to Jackson Javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43971">#43971</a></li>
<li>Warn that some Quartz database schema scripts must be modified
before use <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43958">#43958</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Upgrade to Commons Pool2 2.12.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44173">#44173</a></li>
<li>Upgrade to Couchbase Client 3.7.8 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44269">#44269</a></li>
<li>Upgrade to Groovy 4.0.25 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44174">#44174</a></li>
<li>Upgrade to Hibernate 6.6.8.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44332">#44332</a></li>
<li>Upgrade to HttpClient5 5.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44176">#44176</a></li>
<li>Upgrade to HttpCore5 5.3.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44177">#44177</a></li>
<li>Upgrade to Infinispan 15.0.13.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44178">#44178</a></li>
<li>Upgrade to jOOQ 3.19.19 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44368">#44368</a></li>
<li>Upgrade to Json-smart 2.5.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44264">#44264</a></li>
<li>Upgrade to Maven Clean Plugin 3.4.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44349">#44349</a></li>
<li>Upgrade to Micrometer 1.14.4 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44115">#44115</a></li>
<li>Upgrade to Micrometer Tracing 1.4.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44116">#44116</a></li>
<li>Upgrade to Native Build Tools Plugin 0.10.5 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44179">#44179</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2f53c0abc0"><code>2f53c0a</code></a>
Release v3.4.3</li>
<li><a
href="f99171fe2e"><code>f99171f</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="70e0744c27"><code>70e0744</code></a>
Next development version (v3.3.10-SNAPSHOT)</li>
<li><a
href="07d9db3b80"><code>07d9db3</code></a>
Merge pull request <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44380">#44380</a>
from nosan</li>
<li><a
href="22958097e3"><code>2295809</code></a>
Register Log42J StatusListener</li>
<li><a
href="575655ca39"><code>575655c</code></a>
Upgrade Tomcat 11 smoke tests to Tomcat 11.0.4</li>
<li><a
href="c74397af1a"><code>c74397a</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="c718461450"><code>c718461</code></a>
Protect against NoSuchMethodException on setReadOnly</li>
<li><a
href="7dc9bf29b8"><code>7dc9bf2</code></a>
Upgrade to Testcontainers Redis Module 2.2.4</li>
<li><a
href="7d1fc062bf"><code>7d1fc06</code></a>
Upgrade to Testcontainers 1.20.5</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `org.springframework.boot:spring-boot-starter-security` from
3.4.2 to 3.4.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-boot/releases">org.springframework.boot:spring-boot-starter-security's
releases</a>.</em></p>
<blockquote>
<h2>v3.4.3</h2>
<h2> New Features</h2>
<ul>
<li>Add TWENTY_FOUR to JavaVersion enum <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44209">#44209</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Console output may be lost when using Log4j2 with something that
replaces System.out <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44380">#44380</a></li>
<li>Maven plugin does not consistently use ArgFile for classpath
argument on Windows <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44328">#44328</a></li>
<li>Reactive Jetty web server does not fail fast when configured to use
a server name bundle which Jetty does not support <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44319">#44319</a></li>
<li>When web server application context refresh fails, the original
failure is lost if stopping or destroying the web server throws an
exception <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44317">#44317</a></li>
<li>View resolver for Thymeleaf should back off if spring-webmvc is not
present <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44296">#44296</a></li>
<li>WebServer is not destroyed when ReactiveWebServerApplicationContext
refresh fails <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44294">#44294</a></li>
<li>Non-default DataSource candidates are not considered in
H2ConsoleAutoConfiguration <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44293">#44293</a></li>
<li>Banner placeholder and defaults do not work during development <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44255">#44255</a></li>
<li>Mustache templates return with ISO-8859-1 charset rather than UTF-8
in Content-Type response header <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44193">#44193</a></li>
<li>Servlet EndpointRequest doesn't match web server namespace correctly
<a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44188">#44188</a></li>
<li>java.lang.ClassCastException when using default management security
with WebFlux and health probes enabled <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44052">#44052</a></li>
<li>Logback configuration that relies on inner-classes does not work in
a native image <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44025">#44025</a></li>
<li>IllegalStateException: Unable to register SSL bundle after 3.3.8 or
3.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43989">#43989</a></li>
<li>Metrics and health do not include non-default candidate beans <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43481">#43481</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Document that auto-configuration classes should be identified using
their binary names <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44303">#44303</a></li>
<li>Correct typo in MVC security when explaining when UserDetailsService
auto-configuration will back off <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44301">#44301</a></li>
<li>Link to JarLauncher's javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44170">#44170</a></li>
<li>When using observability annotations, recommend that care is taken
to avoid double instrumentation <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44145">#44145</a></li>
<li>Fix typo in Running Your Application <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44035">#44035</a></li>
<li>Document Kubernetes preStop handler when using a Docker image
without a shell <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44022">#44022</a></li>
<li>Source snippet in Developing Your First Spring Boot Application
section uses the root package <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43983">#43983</a></li>
<li>Correct the location of MyApplication.java in &quot;Developing Your
First Spring Boot Application&quot; <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43975">#43975</a></li>
<li>Add links to Jackson Javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43971">#43971</a></li>
<li>Warn that some Quartz database schema scripts must be modified
before use <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43958">#43958</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Upgrade to Commons Pool2 2.12.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44173">#44173</a></li>
<li>Upgrade to Couchbase Client 3.7.8 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44269">#44269</a></li>
<li>Upgrade to Groovy 4.0.25 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44174">#44174</a></li>
<li>Upgrade to Hibernate 6.6.8.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44332">#44332</a></li>
<li>Upgrade to HttpClient5 5.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44176">#44176</a></li>
<li>Upgrade to HttpCore5 5.3.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44177">#44177</a></li>
<li>Upgrade to Infinispan 15.0.13.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44178">#44178</a></li>
<li>Upgrade to jOOQ 3.19.19 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44368">#44368</a></li>
<li>Upgrade to Json-smart 2.5.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44264">#44264</a></li>
<li>Upgrade to Maven Clean Plugin 3.4.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44349">#44349</a></li>
<li>Upgrade to Micrometer 1.14.4 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44115">#44115</a></li>
<li>Upgrade to Micrometer Tracing 1.4.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44116">#44116</a></li>
<li>Upgrade to Native Build Tools Plugin 0.10.5 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44179">#44179</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2f53c0abc0"><code>2f53c0a</code></a>
Release v3.4.3</li>
<li><a
href="f99171fe2e"><code>f99171f</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="70e0744c27"><code>70e0744</code></a>
Next development version (v3.3.10-SNAPSHOT)</li>
<li><a
href="07d9db3b80"><code>07d9db3</code></a>
Merge pull request <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44380">#44380</a>
from nosan</li>
<li><a
href="22958097e3"><code>2295809</code></a>
Register Log42J StatusListener</li>
<li><a
href="575655ca39"><code>575655c</code></a>
Upgrade Tomcat 11 smoke tests to Tomcat 11.0.4</li>
<li><a
href="c74397af1a"><code>c74397a</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="c718461450"><code>c718461</code></a>
Protect against NoSuchMethodException on setReadOnly</li>
<li><a
href="7dc9bf29b8"><code>7dc9bf2</code></a>
Upgrade to Testcontainers Redis Module 2.2.4</li>
<li><a
href="7d1fc062bf"><code>7d1fc06</code></a>
Upgrade to Testcontainers 1.20.5</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `org.springframework.boot:spring-boot-starter-data-jpa` from
3.4.2 to 3.4.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-boot/releases">org.springframework.boot:spring-boot-starter-data-jpa's
releases</a>.</em></p>
<blockquote>
<h2>v3.4.3</h2>
<h2> New Features</h2>
<ul>
<li>Add TWENTY_FOUR to JavaVersion enum <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44209">#44209</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Console output may be lost when using Log4j2 with something that
replaces System.out <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44380">#44380</a></li>
<li>Maven plugin does not consistently use ArgFile for classpath
argument on Windows <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44328">#44328</a></li>
<li>Reactive Jetty web server does not fail fast when configured to use
a server name bundle which Jetty does not support <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44319">#44319</a></li>
<li>When web server application context refresh fails, the original
failure is lost if stopping or destroying the web server throws an
exception <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44317">#44317</a></li>
<li>View resolver for Thymeleaf should back off if spring-webmvc is not
present <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44296">#44296</a></li>
<li>WebServer is not destroyed when ReactiveWebServerApplicationContext
refresh fails <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44294">#44294</a></li>
<li>Non-default DataSource candidates are not considered in
H2ConsoleAutoConfiguration <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44293">#44293</a></li>
<li>Banner placeholder and defaults do not work during development <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44255">#44255</a></li>
<li>Mustache templates return with ISO-8859-1 charset rather than UTF-8
in Content-Type response header <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44193">#44193</a></li>
<li>Servlet EndpointRequest doesn't match web server namespace correctly
<a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44188">#44188</a></li>
<li>java.lang.ClassCastException when using default management security
with WebFlux and health probes enabled <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44052">#44052</a></li>
<li>Logback configuration that relies on inner-classes does not work in
a native image <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44025">#44025</a></li>
<li>IllegalStateException: Unable to register SSL bundle after 3.3.8 or
3.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43989">#43989</a></li>
<li>Metrics and health do not include non-default candidate beans <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43481">#43481</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Document that auto-configuration classes should be identified using
their binary names <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44303">#44303</a></li>
<li>Correct typo in MVC security when explaining when UserDetailsService
auto-configuration will back off <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44301">#44301</a></li>
<li>Link to JarLauncher's javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44170">#44170</a></li>
<li>When using observability annotations, recommend that care is taken
to avoid double instrumentation <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44145">#44145</a></li>
<li>Fix typo in Running Your Application <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44035">#44035</a></li>
<li>Document Kubernetes preStop handler when using a Docker image
without a shell <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44022">#44022</a></li>
<li>Source snippet in Developing Your First Spring Boot Application
section uses the root package <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43983">#43983</a></li>
<li>Correct the location of MyApplication.java in &quot;Developing Your
First Spring Boot Application&quot; <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43975">#43975</a></li>
<li>Add links to Jackson Javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43971">#43971</a></li>
<li>Warn that some Quartz database schema scripts must be modified
before use <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43958">#43958</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Upgrade to Commons Pool2 2.12.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44173">#44173</a></li>
<li>Upgrade to Couchbase Client 3.7.8 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44269">#44269</a></li>
<li>Upgrade to Groovy 4.0.25 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44174">#44174</a></li>
<li>Upgrade to Hibernate 6.6.8.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44332">#44332</a></li>
<li>Upgrade to HttpClient5 5.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44176">#44176</a></li>
<li>Upgrade to HttpCore5 5.3.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44177">#44177</a></li>
<li>Upgrade to Infinispan 15.0.13.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44178">#44178</a></li>
<li>Upgrade to jOOQ 3.19.19 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44368">#44368</a></li>
<li>Upgrade to Json-smart 2.5.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44264">#44264</a></li>
<li>Upgrade to Maven Clean Plugin 3.4.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44349">#44349</a></li>
<li>Upgrade to Micrometer 1.14.4 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44115">#44115</a></li>
<li>Upgrade to Micrometer Tracing 1.4.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44116">#44116</a></li>
<li>Upgrade to Native Build Tools Plugin 0.10.5 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44179">#44179</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2f53c0abc0"><code>2f53c0a</code></a>
Release v3.4.3</li>
<li><a
href="f99171fe2e"><code>f99171f</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="70e0744c27"><code>70e0744</code></a>
Next development version (v3.3.10-SNAPSHOT)</li>
<li><a
href="07d9db3b80"><code>07d9db3</code></a>
Merge pull request <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44380">#44380</a>
from nosan</li>
<li><a
href="22958097e3"><code>2295809</code></a>
Register Log42J StatusListener</li>
<li><a
href="575655ca39"><code>575655c</code></a>
Upgrade Tomcat 11 smoke tests to Tomcat 11.0.4</li>
<li><a
href="c74397af1a"><code>c74397a</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="c718461450"><code>c718461</code></a>
Protect against NoSuchMethodException on setReadOnly</li>
<li><a
href="7dc9bf29b8"><code>7dc9bf2</code></a>
Upgrade to Testcontainers Redis Module 2.2.4</li>
<li><a
href="7d1fc062bf"><code>7d1fc06</code></a>
Upgrade to Testcontainers 1.20.5</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `org.springframework.boot:spring-boot-starter-oauth2-client`
from 3.4.2 to 3.4.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-boot/releases">org.springframework.boot:spring-boot-starter-oauth2-client's
releases</a>.</em></p>
<blockquote>
<h2>v3.4.3</h2>
<h2> New Features</h2>
<ul>
<li>Add TWENTY_FOUR to JavaVersion enum <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44209">#44209</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Console output may be lost when using Log4j2 with something that
replaces System.out <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44380">#44380</a></li>
<li>Maven plugin does not consistently use ArgFile for classpath
argument on Windows <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44328">#44328</a></li>
<li>Reactive Jetty web server does not fail fast when configured to use
a server name bundle which Jetty does not support <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44319">#44319</a></li>
<li>When web server application context refresh fails, the original
failure is lost if stopping or destroying the web server throws an
exception <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44317">#44317</a></li>
<li>View resolver for Thymeleaf should back off if spring-webmvc is not
present <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44296">#44296</a></li>
<li>WebServer is not destroyed when ReactiveWebServerApplicationContext
refresh fails <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44294">#44294</a></li>
<li>Non-default DataSource candidates are not considered in
H2ConsoleAutoConfiguration <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44293">#44293</a></li>
<li>Banner placeholder and defaults do not work during development <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44255">#44255</a></li>
<li>Mustache templates return with ISO-8859-1 charset rather than UTF-8
in Content-Type response header <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44193">#44193</a></li>
<li>Servlet EndpointRequest doesn't match web server namespace correctly
<a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44188">#44188</a></li>
<li>java.lang.ClassCastException when using default management security
with WebFlux and health probes enabled <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44052">#44052</a></li>
<li>Logback configuration that relies on inner-classes does not work in
a native image <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44025">#44025</a></li>
<li>IllegalStateException: Unable to register SSL bundle after 3.3.8 or
3.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43989">#43989</a></li>
<li>Metrics and health do not include non-default candidate beans <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43481">#43481</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Document that auto-configuration classes should be identified using
their binary names <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44303">#44303</a></li>
<li>Correct typo in MVC security when explaining when UserDetailsService
auto-configuration will back off <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44301">#44301</a></li>
<li>Link to JarLauncher's javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44170">#44170</a></li>
<li>When using observability annotations, recommend that care is taken
to avoid double instrumentation <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44145">#44145</a></li>
<li>Fix typo in Running Your Application <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44035">#44035</a></li>
<li>Document Kubernetes preStop handler when using a Docker image
without a shell <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44022">#44022</a></li>
<li>Source snippet in Developing Your First Spring Boot Application
section uses the root package <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43983">#43983</a></li>
<li>Correct the location of MyApplication.java in &quot;Developing Your
First Spring Boot Application&quot; <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43975">#43975</a></li>
<li>Add links to Jackson Javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43971">#43971</a></li>
<li>Warn that some Quartz database schema scripts must be modified
before use <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43958">#43958</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Upgrade to Commons Pool2 2.12.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44173">#44173</a></li>
<li>Upgrade to Couchbase Client 3.7.8 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44269">#44269</a></li>
<li>Upgrade to Groovy 4.0.25 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44174">#44174</a></li>
<li>Upgrade to Hibernate 6.6.8.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44332">#44332</a></li>
<li>Upgrade to HttpClient5 5.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44176">#44176</a></li>
<li>Upgrade to HttpCore5 5.3.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44177">#44177</a></li>
<li>Upgrade to Infinispan 15.0.13.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44178">#44178</a></li>
<li>Upgrade to jOOQ 3.19.19 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44368">#44368</a></li>
<li>Upgrade to Json-smart 2.5.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44264">#44264</a></li>
<li>Upgrade to Maven Clean Plugin 3.4.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44349">#44349</a></li>
<li>Upgrade to Micrometer 1.14.4 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44115">#44115</a></li>
<li>Upgrade to Micrometer Tracing 1.4.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44116">#44116</a></li>
<li>Upgrade to Native Build Tools Plugin 0.10.5 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44179">#44179</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2f53c0abc0"><code>2f53c0a</code></a>
Release v3.4.3</li>
<li><a
href="f99171fe2e"><code>f99171f</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="70e0744c27"><code>70e0744</code></a>
Next development version (v3.3.10-SNAPSHOT)</li>
<li><a
href="07d9db3b80"><code>07d9db3</code></a>
Merge pull request <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44380">#44380</a>
from nosan</li>
<li><a
href="22958097e3"><code>2295809</code></a>
Register Log42J StatusListener</li>
<li><a
href="575655ca39"><code>575655c</code></a>
Upgrade Tomcat 11 smoke tests to Tomcat 11.0.4</li>
<li><a
href="c74397af1a"><code>c74397a</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="c718461450"><code>c718461</code></a>
Protect against NoSuchMethodException on setReadOnly</li>
<li><a
href="7dc9bf29b8"><code>7dc9bf2</code></a>
Upgrade to Testcontainers Redis Module 2.2.4</li>
<li><a
href="7d1fc062bf"><code>7d1fc06</code></a>
Upgrade to Testcontainers 1.20.5</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `org.springframework.boot:spring-boot-starter-test` from 3.4.2
to 3.4.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-boot/releases">org.springframework.boot:spring-boot-starter-test's
releases</a>.</em></p>
<blockquote>
<h2>v3.4.3</h2>
<h2> New Features</h2>
<ul>
<li>Add TWENTY_FOUR to JavaVersion enum <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44209">#44209</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Console output may be lost when using Log4j2 with something that
replaces System.out <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44380">#44380</a></li>
<li>Maven plugin does not consistently use ArgFile for classpath
argument on Windows <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44328">#44328</a></li>
<li>Reactive Jetty web server does not fail fast when configured to use
a server name bundle which Jetty does not support <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44319">#44319</a></li>
<li>When web server application context refresh fails, the original
failure is lost if stopping or destroying the web server throws an
exception <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44317">#44317</a></li>
<li>View resolver for Thymeleaf should back off if spring-webmvc is not
present <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44296">#44296</a></li>
<li>WebServer is not destroyed when ReactiveWebServerApplicationContext
refresh fails <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44294">#44294</a></li>
<li>Non-default DataSource candidates are not considered in
H2ConsoleAutoConfiguration <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44293">#44293</a></li>
<li>Banner placeholder and defaults do not work during development <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44255">#44255</a></li>
<li>Mustache templates return with ISO-8859-1 charset rather than UTF-8
in Content-Type response header <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44193">#44193</a></li>
<li>Servlet EndpointRequest doesn't match web server namespace correctly
<a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44188">#44188</a></li>
<li>java.lang.ClassCastException when using default management security
with WebFlux and health probes enabled <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44052">#44052</a></li>
<li>Logback configuration that relies on inner-classes does not work in
a native image <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44025">#44025</a></li>
<li>IllegalStateException: Unable to register SSL bundle after 3.3.8 or
3.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43989">#43989</a></li>
<li>Metrics and health do not include non-default candidate beans <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43481">#43481</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Document that auto-configuration classes should be identified using
their binary names <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44303">#44303</a></li>
<li>Correct typo in MVC security when explaining when UserDetailsService
auto-configuration will back off <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44301">#44301</a></li>
<li>Link to JarLauncher's javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44170">#44170</a></li>
<li>When using observability annotations, recommend that care is taken
to avoid double instrumentation <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44145">#44145</a></li>
<li>Fix typo in Running Your Application <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44035">#44035</a></li>
<li>Document Kubernetes preStop handler when using a Docker image
without a shell <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44022">#44022</a></li>
<li>Source snippet in Developing Your First Spring Boot Application
section uses the root package <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43983">#43983</a></li>
<li>Correct the location of MyApplication.java in &quot;Developing Your
First Spring Boot Application&quot; <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43975">#43975</a></li>
<li>Add links to Jackson Javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43971">#43971</a></li>
<li>Warn that some Quartz database schema scripts must be modified
before use <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43958">#43958</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Upgrade to Commons Pool2 2.12.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44173">#44173</a></li>
<li>Upgrade to Couchbase Client 3.7.8 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44269">#44269</a></li>
<li>Upgrade to Groovy 4.0.25 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44174">#44174</a></li>
<li>Upgrade to Hibernate 6.6.8.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44332">#44332</a></li>
<li>Upgrade to HttpClient5 5.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44176">#44176</a></li>
<li>Upgrade to HttpCore5 5.3.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44177">#44177</a></li>
<li>Upgrade to Infinispan 15.0.13.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44178">#44178</a></li>
<li>Upgrade to jOOQ 3.19.19 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44368">#44368</a></li>
<li>Upgrade to Json-smart 2.5.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44264">#44264</a></li>
<li>Upgrade to Maven Clean Plugin 3.4.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44349">#44349</a></li>
<li>Upgrade to Micrometer 1.14.4 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44115">#44115</a></li>
<li>Upgrade to Micrometer Tracing 1.4.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44116">#44116</a></li>
<li>Upgrade to Native Build Tools Plugin 0.10.5 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44179">#44179</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2f53c0abc0"><code>2f53c0a</code></a>
Release v3.4.3</li>
<li><a
href="f99171fe2e"><code>f99171f</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="70e0744c27"><code>70e0744</code></a>
Next development version (v3.3.10-SNAPSHOT)</li>
<li><a
href="07d9db3b80"><code>07d9db3</code></a>
Merge pull request <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44380">#44380</a>
from nosan</li>
<li><a
href="22958097e3"><code>2295809</code></a>
Register Log42J StatusListener</li>
<li><a
href="575655ca39"><code>575655c</code></a>
Upgrade Tomcat 11 smoke tests to Tomcat 11.0.4</li>
<li><a
href="c74397af1a"><code>c74397a</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="c718461450"><code>c718461</code></a>
Protect against NoSuchMethodException on setReadOnly</li>
<li><a
href="7dc9bf29b8"><code>7dc9bf2</code></a>
Upgrade to Testcontainers Redis Module 2.2.4</li>
<li><a
href="7d1fc062bf"><code>7d1fc06</code></a>
Upgrade to Testcontainers 1.20.5</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `org.springframework.boot:spring-boot-starter-actuator` from
3.4.2 to 3.4.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-boot/releases">org.springframework.boot:spring-boot-starter-actuator's
releases</a>.</em></p>
<blockquote>
<h2>v3.4.3</h2>
<h2> New Features</h2>
<ul>
<li>Add TWENTY_FOUR to JavaVersion enum <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44209">#44209</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Console output may be lost when using Log4j2 with something that
replaces System.out <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44380">#44380</a></li>
<li>Maven plugin does not consistently use ArgFile for classpath
argument on Windows <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44328">#44328</a></li>
<li>Reactive Jetty web server does not fail fast when configured to use
a server name bundle which Jetty does not support <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44319">#44319</a></li>
<li>When web server application context refresh fails, the original
failure is lost if stopping or destroying the web server throws an
exception <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44317">#44317</a></li>
<li>View resolver for Thymeleaf should back off if spring-webmvc is not
present <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44296">#44296</a></li>
<li>WebServer is not destroyed when ReactiveWebServerApplicationContext
refresh fails <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44294">#44294</a></li>
<li>Non-default DataSource candidates are not considered in
H2ConsoleAutoConfiguration <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44293">#44293</a></li>
<li>Banner placeholder and defaults do not work during development <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44255">#44255</a></li>
<li>Mustache templates return with ISO-8859-1 charset rather than UTF-8
in Content-Type response header <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44193">#44193</a></li>
<li>Servlet EndpointRequest doesn't match web server namespace correctly
<a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44188">#44188</a></li>
<li>java.lang.ClassCastException when using default management security
with WebFlux and health probes enabled <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44052">#44052</a></li>
<li>Logback configuration that relies on inner-classes does not work in
a native image <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44025">#44025</a></li>
<li>IllegalStateException: Unable to register SSL bundle after 3.3.8 or
3.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43989">#43989</a></li>
<li>Metrics and health do not include non-default candidate beans <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43481">#43481</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Document that auto-configuration classes should be identified using
their binary names <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44303">#44303</a></li>
<li>Correct typo in MVC security when explaining when UserDetailsService
auto-configuration will back off <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44301">#44301</a></li>
<li>Link to JarLauncher's javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44170">#44170</a></li>
<li>When using observability annotations, recommend that care is taken
to avoid double inst...

_Description has been truncated_

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-22 23:37:53 +00:00
dependabot[bot]
219adb99ec Bump org.springframework.security:spring-security-saml2-service-provider from 6.4.2 to 6.4.3 (#3009)
Bumps
[org.springframework.security:spring-security-saml2-service-provider](https://github.com/spring-projects/spring-security)
from 6.4.2 to 6.4.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-security/releases">org.springframework.security:spring-security-saml2-service-provider's
releases</a>.</em></p>
<blockquote>
<h2>6.4.3</h2>
<h2> New Features</h2>
<ul>
<li>Add Support disableDefaultRegistrationPage to WebAuthnDsl <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16395">#16395</a></li>
</ul>
<h2>🪲 Bug Fixes</h2>
<ul>
<li><code>withValue</code> used incorrectly <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16527">#16527</a></li>
<li>Fix for JdbcOneTimeTokenService cleanupExpiredTokens failing with
PostgreSQL <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16344">#16344</a></li>
<li>Fix GenerateOneTimeTokenWebFilter double publish of
chain.filter(...) <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16459">#16459</a></li>
<li>Fix Kotlin DSL webAuthn { } <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16338">#16338</a></li>
<li>Fix loader has changed while resolving nodes in
WebAuthnWebDriverTests <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16463">#16463</a></li>
<li>Fix logoutRequestRepository not set on
Saml2RelyingPartyInitiatedLogoutSuccessHandler <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16310">#16310</a></li>
<li>Implement <code>Serializable</code> for WebAuthnAuthentication <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16285">#16285</a></li>
<li>Make AuthorizationDecision Serializable <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16544">#16544</a></li>
<li>Make PublicKeyCredentialRequestOptions Serializable Backport <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16584">#16584</a></li>
<li>Make Saml2AuthenticationToken Serializable <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16287">#16287</a></li>
<li>Make WebAuthnAuthentication Serializable <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16273">#16273</a></li>
<li>Make WebAuthnAuthenticationRequestToken Serializable <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16602">#16602</a></li>
<li>Make WebAuthnAuthenticationTokenRequest Serializable <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16481">#16481</a></li>
<li>Misconfigured OAuth2LoginAuthenticationFilter when combining OAuth2
login and OAuth2 client configuration <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16466">#16466</a></li>
<li>OTT Should Use non-static member to capture the last OneTimeToken <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16471">#16471</a></li>
<li>webauthn js should ensure allowCredentials[].id is an ArrayBuffer <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16440">#16440</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Bump ch.qos.logback:logback-classic from 1.5.15 to 1.5.16 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16364">#16364</a></li>
<li>Bump com.nimbusds:oauth2-oidc-sdk from 9.43.5 to 9.43.6 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16598">#16598</a></li>
<li>Bump com.webauthn4j:webauthn4j-core from 0.28.4.RELEASE to
0.28.5.RELEASE <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16523">#16523</a></li>
<li>Bump io.micrometer:micrometer-observation from 1.14.3 to 1.14.4 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16565">#16565</a></li>
<li>Bump io.mockk:mockk from 1.13.14 to 1.13.16 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16399">#16399</a></li>
<li>Bump io.projectreactor:reactor-bom from 2023.0.14 to 2023.0.15 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16576">#16576</a></li>
<li>Bump io.rsocket:rsocket-bom from 1.1.4 to 1.1.5 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16534">#16534</a></li>
<li>Bump org.hibernate.orm:hibernate-core from 6.6.7.Final to
6.6.8.Final <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16610">#16610</a></li>
<li>Bump org.junit:junit-bom from 5.11.3 to 5.11.4 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16292">#16292</a></li>
<li>Bump org.springframework.data:spring-data-bom from 2024.1.2 to
2024.1.3 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16611">#16611</a></li>
<li>Bump org.springframework.ldap:spring-ldap-core from 3.2.10 to 3.2.11
<a
href="https://redirect.github.com/spring-projects/spring-security/pull/16597">#16597</a></li>
<li>Bump org.springframework:spring-framework-bom from 6.2.2 to 6.2.3 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16599">#16599</a></li>
<li>Update to oauth2-oidc-sdk 9.43.5 <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16583">#16583</a></li>
</ul>
<h2>🔩 Build Updates</h2>
<ul>
<li>Add TestBytes <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16461">#16461</a></li>
<li>Troubleshoot missing GChat notifications <a
href="https://redirect.github.com/spring-projects/spring-security/issues/16424">#16424</a></li>
</ul>
<h2>❤️ Contributors</h2>
<p>Thank you to all the contributors who worked on this release:</p>
<p><a href="https://github.com/Kehrlann"><code>@​Kehrlann</code></a>, <a
href="https://github.com/NeoTraveler"><code>@​NeoTraveler</code></a>, <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot],
<a
href="https://github.com/franticticktick"><code>@​franticticktick</code></a>,
<a href="https://github.com/making"><code>@​making</code></a>, and <a
href="https://github.com/ngocnhan-tran1996"><code>@​ngocnhan-tran1996</code></a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e7431a3a72"><code>e7431a3</code></a>
Release 6.4.3</li>
<li><a
href="d2f825bc74"><code>d2f825b</code></a>
Bump org.springframework.data:spring-data-bom from 2024.1.2 to
2024.1.3</li>
<li><a
href="6fcbc0ea2a"><code>6fcbc0e</code></a>
Bump org.hibernate.orm:hibernate-core from 6.6.7.Final to
6.6.8.Final</li>
<li><a
href="efe50dd0eb"><code>efe50dd</code></a>
Merge branch '6.3.x' into 6.4.x</li>
<li><a
href="06026684e5"><code>0602668</code></a>
Bump org.springframework.data:spring-data-bom from 2024.0.8 to
2024.0.9</li>
<li><a
href="946812691e"><code>9468126</code></a>
Make AuthenticatorAttestation Serializable</li>
<li><a
href="b5a4218a0b"><code>b5a4218</code></a>
Make WebAuthnAuthenticationRequestToken Serializable</li>
<li><a
href="9e1a573531"><code>9e1a573</code></a>
Bump org.springframework:spring-framework-bom from 6.2.2 to 6.2.3</li>
<li><a
href="309daf565d"><code>309daf5</code></a>
Merge branch '6.3.x' into 6.4.x</li>
<li><a
href="c3d45ae529"><code>c3d45ae</code></a>
Bump org.springframework:spring-framework-bom from 6.1.16 to 6.1.17</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-security/compare/6.4.2...6.4.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.springframework.security:spring-security-saml2-service-provider&package-manager=gradle&previous-version=6.4.2&new-version=6.4.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-22 23:37:22 +00:00
dependabot[bot]
23a7b11a74 Bump org.springframework.session:spring-session-core from 3.4.1 to 3.4.2 (#3014)
Bumps
[org.springframework.session:spring-session-core](https://github.com/spring-projects/spring-session)
from 3.4.1 to 3.4.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-session/releases">org.springframework.session:spring-session-core's
releases</a>.</em></p>
<blockquote>
<h2>3.4.2</h2>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Bump ch-qos-logback from 1.5.12 to 1.5.13 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3304">#3304</a></li>
<li>Bump ch-qos-logback from 1.5.13 to 1.5.14 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3306">#3306</a></li>
<li>Bump ch-qos-logback from 1.5.14 to 1.5.15 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3309">#3309</a></li>
<li>Bump ch-qos-logback from 1.5.15 to 1.5.16 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3315">#3315</a></li>
<li>Bump io.projectreactor:reactor-bom from 2023.0.13 to 2023.0.14 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3324">#3324</a></li>
<li>Bump io.projectreactor:reactor-bom from 2023.0.14 to 2023.0.15 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3336">#3336</a></li>
<li>Bump io.projectreactor:reactor-core from 3.6.13 to 3.6.14 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3337">#3337</a></li>
<li>Bump org-springframework-boot from 3.3.6 to 3.3.7 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3307">#3307</a></li>
<li>Bump org-springframework-boot from 3.3.7 to 3.3.8 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3332">#3332</a></li>
<li>Bump org.postgresql:postgresql from 42.7.4 to 42.7.5 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3325">#3325</a></li>
<li>Bump org.springframework.data:spring-data-bom from 2024.1.1 to
2024.1.2 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3328">#3328</a></li>
<li>Bump org.springframework.data:spring-data-bom from 2024.1.2 to
2024.1.3 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3339">#3339</a></li>
<li>Bump org.springframework:spring-framework-bom from 6.2.1 to 6.2.2 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3326">#3326</a></li>
<li>Bump org.springframework:spring-framework-bom from 6.2.2 to 6.2.3 <a
href="https://redirect.github.com/spring-projects/spring-session/pull/3338">#3338</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d600674f13"><code>d600674</code></a>
Release 3.4.2</li>
<li><a
href="39dd594d66"><code>39dd594</code></a>
Bump org.springframework.data:spring-data-bom from 2024.1.2 to
2024.1.3</li>
<li><a
href="1f7439e28a"><code>1f7439e</code></a>
Bump org.springframework.data:spring-data-bom from 2024.1.1 to
2024.1.2</li>
<li><a
href="5fa21f19a3"><code>5fa21f1</code></a>
Bump org.springframework:spring-framework-bom from 6.2.2 to 6.2.3</li>
<li><a
href="8a6a5c0d0e"><code>8a6a5c0</code></a>
Bump io.projectreactor:reactor-core from 3.6.13 to 3.6.14</li>
<li><a
href="495dc471bd"><code>495dc47</code></a>
Bump io.projectreactor:reactor-bom from 2023.0.14 to 2023.0.15</li>
<li><a
href="e902d3f11a"><code>e902d3f</code></a>
Bump org-springframework-boot from 3.3.7 to 3.3.8</li>
<li><a
href="6ad1989745"><code>6ad1989</code></a>
Bump org.springframework:spring-framework-bom from 6.2.1 to 6.2.2</li>
<li><a
href="4283df46ac"><code>4283df4</code></a>
Bump org.postgresql:postgresql from 42.7.4 to 42.7.5</li>
<li><a
href="13088c6ac7"><code>13088c6</code></a>
Bump io.projectreactor:reactor-bom from 2023.0.13 to 2023.0.14</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-session/compare/3.4.1...3.4.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.springframework.session:spring-session-core&package-manager=gradle&previous-version=3.4.1&new-version=3.4.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-22 23:28:21 +00:00
Abdur Rahman
6ac804e994 Feature/convert to grayscale (#3003)
# Description of Changes

Please provide a summary of the changes, including:

- What was changed
  -Grayscale Image Compression:
Modified the compressImagesInPDF method to optionally convert images to
grayscale during compression. This is
achieved by checking if the grayScale flag is true (or if the image is
already in grayscale) and then processing the image
   accordingly.

   UI Update:
Updated the compress-pdf.html file by adding a checkbox for grayscale
compression. Additionally, a new translation
key compress.grayscale.label with the text "Apply Grayscale for
Compression" has been added across all supported
   languages. 
- Why the change was made
  -Enhanced Compression Options:
This feature provides users with an option to compress images in PDFs
more effectively by reducing the color complexity, which can lead to
smaller file sizes.

Improved Flexibility:
It allows users to decide whether they want to maintain the original
color images or opt for a grayscale version.
- Any challenges encountered
- The translation for compress.grayscale.label was generated using an
automated translator, so it might not be completely accurate and could
require further review.

Closes #2603

---

## Checklist

### General

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-22 23:08:07 +00:00
Giuseppe Ravasio
09ff207888 Adding font-liberation and font-linux-libertine to fat image (#3001) (#3005)
# Description of Changes

Please provide a summary of the changes, including:

installing font-liberation and font-linux-libertine to alpine linux
system

Closes #3001 

---

## Checklist

### General

- [Y] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [Y] I have performed a self-review of my own code
- [Y] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [Y] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-22 22:55:32 +00:00
dependabot[bot]
880ca6af7f Bump actions/create-github-app-token from 1.11.3 to 1.11.5 (#3008)
Bumps
[actions/create-github-app-token](https://github.com/actions/create-github-app-token)
from 1.11.3 to 1.11.5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/create-github-app-token/releases">actions/create-github-app-token's
releases</a>.</em></p>
<blockquote>
<h2>v1.11.5</h2>
<h2><a
href="https://github.com/actions/create-github-app-token/compare/v1.11.4...v1.11.5">1.11.5</a>
(2025-02-15)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> bump <code>@​octokit/request</code> from
9.2.0 to 9.2.2 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/209">#209</a>)
(<a
href="8cedd97af1">8cedd97</a>),
closes <a
href="https://redirect.github.com/actions/create-github-app-token/issues/740">#740</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/738">#738</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/740">#740</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/737">#737</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/738">#738</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/736">#736</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/735">#735</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/734">#734</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/733">#733</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/732">#732</a></li>
<li><strong>deps:</strong> bump <code>@​octokit/request-error</code>
from 6.1.6 to 6.1.7 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/208">#208</a>)
(<a
href="415f6a523d">415f6a5</a>),
closes <a
href="https://redirect.github.com/actions/create-github-app-token/issues/494">#494</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/491">#491</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/490">#490</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/488">#488</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/486">#486</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/487">#487</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/485">#485</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/484">#484</a></li>
</ul>
<h2>v1.11.4</h2>
<h2><a
href="https://github.com/actions/create-github-app-token/compare/v1.11.3...v1.11.4">1.11.4</a>
(2025-02-15)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> bump <code>@​octokit/endpoint</code> from
10.1.1 to 10.1.3 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/207">#207</a>)
(<a
href="d30def842e">d30def8</a>),
closes <a
href="https://redirect.github.com/actions/create-github-app-token/issues/507">#507</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/514">#514</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/512">#512</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/511">#511</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/509">#509</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/508">#508</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/507">#507</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/506">#506</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/505">#505</a>
<a
href="https://redirect.github.com/actions/create-github-app-token/issues/504">#504</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="0d564482f0"><code>0d56448</code></a>
build(release): 1.11.5 [skip ci]</li>
<li><a
href="8cedd97af1"><code>8cedd97</code></a>
fix(deps): bump <code>@​octokit/request</code> from 9.2.0 to 9.2.2 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/209">#209</a>)</li>
<li><a
href="415f6a523d"><code>415f6a5</code></a>
fix(deps): bump <code>@​octokit/request-error</code> from 6.1.6 to 6.1.7
(<a
href="https://redirect.github.com/actions/create-github-app-token/issues/208">#208</a>)</li>
<li><a
href="c14f92a8f9"><code>c14f92a</code></a>
build(release): 1.11.4 [skip ci]</li>
<li><a
href="d30def842e"><code>d30def8</code></a>
fix(deps): bump <code>@​octokit/endpoint</code> from 10.1.1 to 10.1.3
(<a
href="https://redirect.github.com/actions/create-github-app-token/issues/207">#207</a>)</li>
<li><a
href="a5be4722a6"><code>a5be472</code></a>
build(deps-dev): bump esbuild from 0.24.2 to 0.25.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/206">#206</a>)</li>
<li>See full diff in <a
href="67e27a7eb7...0d564482f0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/create-github-app-token&package-manager=github_actions&previous-version=1.11.3&new-version=1.11.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ludy <Ludy87@users.noreply.github.com>
2025-02-22 22:55:09 +00:00
dependabot[bot]
3797a6827c Bump docker/build-push-action from 6.13.0 to 6.14.0 (#3012)
Bumps
[docker/build-push-action](https://github.com/docker/build-push-action)
from 6.13.0 to 6.14.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/docker/build-push-action/releases">docker/build-push-action's
releases</a>.</em></p>
<blockquote>
<h2>v6.14.0</h2>
<ul>
<li>Bump <code>@​docker/actions-toolkit</code> from 0.53.0 to 0.55.0 in
<a
href="https://redirect.github.com/docker/build-push-action/pull/1324">docker/build-push-action#1324</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/docker/build-push-action/compare/v6.13.0...v6.14.0">https://github.com/docker/build-push-action/compare/v6.13.0...v6.14.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="0adf995921"><code>0adf995</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/build-push-action/issues/1324">#1324</a>
from docker/dependabot/npm_and_yarn/docker/actions-t...</li>
<li><a
href="d88cd289df"><code>d88cd28</code></a>
chore: update generated content</li>
<li><a
href="3d09a6bd70"><code>3d09a6b</code></a>
chore(deps): Bump <code>@​docker/actions-toolkit</code> from 0.53.0 to
0.55.0</li>
<li>See full diff in <a
href="ca877d9245...0adf995921">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=docker/build-push-action&package-manager=github_actions&previous-version=6.13.0&new-version=6.14.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-22 22:54:43 +00:00
dependabot[bot]
0f5d95661c Bump sigstore/cosign-installer from 3.8.0 to 3.8.1 (#3010)
Bumps
[sigstore/cosign-installer](https://github.com/sigstore/cosign-installer)
from 3.8.0 to 3.8.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sigstore/cosign-installer/releases">sigstore/cosign-installer's
releases</a>.</em></p>
<blockquote>
<h2>v3.8.1</h2>
<h2>What's Changed</h2>
<ul>
<li>use cosign 2.4.3 and other updates by <a
href="https://github.com/cpanato"><code>@​cpanato</code></a> in <a
href="https://redirect.github.com/sigstore/cosign-installer/pull/182">sigstore/cosign-installer#182</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/sigstore/cosign-installer/compare/v3...v3.8.1">https://github.com/sigstore/cosign-installer/compare/v3...v3.8.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d7d6bc7722"><code>d7d6bc7</code></a>
use cosign 2.4.3 and other updates (<a
href="https://redirect.github.com/sigstore/cosign-installer/issues/182">#182</a>)</li>
<li>See full diff in <a
href="c56c2d3e59...d7d6bc7722">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=sigstore/cosign-installer&package-manager=github_actions&previous-version=3.8.0&new-version=3.8.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-22 22:49:28 +00:00
dependabot[bot]
d5aea7f0b6 Bump ch.qos.logback:logback-core from 1.5.15 to 1.5.16 (#3011)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [ch.qos.logback:logback-core](https://github.com/qos-ch/logback)
from 1.5.15 to 1.5.16.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/qos-ch/logback/releases">ch.qos.logback:logback-core's
releases</a>.</em></p>
<blockquote>
<h2>Logback 1.5.16</h2>
<p><strong>2025-01-05 Release of logback version 1.5.16</strong></p>
<p>• In order to ease the migration of configuration files depending on
JaninoEventEvaluator, logback-classic will emit a warning about the
removal of JaninoEventEvaluator in version 1.5.13 and suggest an online
migration tool.</p>
<p>• A bit-wise identical binary of this version can be reproduced by
building from source code at commit
74c9ebd0e784d9e9ffc6c627cf5016d0157956b2 associated with the tag
v_1.5.16. Release built using Java &quot;21&quot; 2023-10-17 LTS build
21.0.1.+12-LTS-29 under Linux Debian 11.6.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="74c9ebd0e7"><code>74c9ebd</code></a>
prepare release 1.5.16</li>
<li><a
href="9308a58e65"><code>9308a58</code></a>
javadocs structure changed</li>
<li><a
href="8935470736"><code>8935470</code></a>
adapt test to SLF4J version 2.0.16</li>
<li><a
href="cb603698a1"><code>cb60369</code></a>
addded StubEventEvaluator as default class for evaluator element so as
to dir...</li>
<li><a
href="1da2f171dc"><code>1da2f17</code></a>
bump jxr version</li>
<li><a
href="5bde644c26"><code>5bde644</code></a>
bump slf4j version to 2.0.16</li>
<li><a
href="aa2ebae414"><code>aa2ebae</code></a>
remove stax related code</li>
<li><a
href="80db86b548"><code>80db86b</code></a>
fix issues/860</li>
<li><a
href="a8a2303ba3"><code>a8a2303</code></a>
start work on 1.5.16-SNAPSHOT</li>
<li><a
href="bf14c2c7f6"><code>bf14c2c</code></a>
minor javadoc update</li>
<li>See full diff in <a
href="https://github.com/qos-ch/logback/compare/v_1.5.15...v_1.5.16">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ch.qos.logback:logback-core&package-manager=gradle&previous-version=1.5.15&new-version=1.5.16)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-22 22:49:13 +00:00
dependabot[bot]
0412263d01 Bump ch.qos.logback:logback-classic from 1.5.15 to 1.5.16 (#3015)
Bumps
[ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from
1.5.15 to 1.5.16.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/qos-ch/logback/releases">ch.qos.logback:logback-classic's
releases</a>.</em></p>
<blockquote>
<h2>Logback 1.5.16</h2>
<p><strong>2025-01-05 Release of logback version 1.5.16</strong></p>
<p>• In order to ease the migration of configuration files depending on
JaninoEventEvaluator, logback-classic will emit a warning about the
removal of JaninoEventEvaluator in version 1.5.13 and suggest an online
migration tool.</p>
<p>• A bit-wise identical binary of this version can be reproduced by
building from source code at commit
74c9ebd0e784d9e9ffc6c627cf5016d0157956b2 associated with the tag
v_1.5.16. Release built using Java &quot;21&quot; 2023-10-17 LTS build
21.0.1.+12-LTS-29 under Linux Debian 11.6.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="74c9ebd0e7"><code>74c9ebd</code></a>
prepare release 1.5.16</li>
<li><a
href="9308a58e65"><code>9308a58</code></a>
javadocs structure changed</li>
<li><a
href="8935470736"><code>8935470</code></a>
adapt test to SLF4J version 2.0.16</li>
<li><a
href="cb603698a1"><code>cb60369</code></a>
addded StubEventEvaluator as default class for evaluator element so as
to dir...</li>
<li><a
href="1da2f171dc"><code>1da2f17</code></a>
bump jxr version</li>
<li><a
href="5bde644c26"><code>5bde644</code></a>
bump slf4j version to 2.0.16</li>
<li><a
href="aa2ebae414"><code>aa2ebae</code></a>
remove stax related code</li>
<li><a
href="80db86b548"><code>80db86b</code></a>
fix issues/860</li>
<li><a
href="a8a2303ba3"><code>a8a2303</code></a>
start work on 1.5.16-SNAPSHOT</li>
<li><a
href="bf14c2c7f6"><code>bf14c2c</code></a>
minor javadoc update</li>
<li>See full diff in <a
href="https://github.com/qos-ch/logback/compare/v_1.5.15...v_1.5.16">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ch.qos.logback:logback-classic&package-manager=gradle&previous-version=1.5.15&new-version=1.5.16)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-22 22:47:46 +00:00
stirlingbot[bot]
d3d9158641 🌐 Sync Translations + Update README Progress Table (#3025)
### Description of Changes

This Pull Request was automatically generated to synchronize updates to
translation files and documentation. Below are the details of the
changes made:

#### **1. Synchronization of Translation Files**
- Updated translation files (`messages_*.properties`) to reflect changes
in the reference file `messages_en_GB.properties`.
- Ensured consistency and synchronization across all supported language
files.
- Highlighted any missing or incomplete translations.

#### **2. Update README.md**
- Generated the translation progress table in `README.md`.
- Added a summary of the current translation status for all supported
languages.
- Included up-to-date statistics on translation coverage.

#### **Why these changes are necessary**
- Keeps translation files aligned with the latest reference updates.
- Ensures the documentation reflects the current translation progress.

---

Auto-generated by [create-pull-request][1].

[1]: https://github.com/peter-evans/create-pull-request

Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-02-22 22:47:08 +00:00
stirlingbot[bot]
c785fb20d1 Update 3rd Party Licenses (#3024)
Auto-generated by StirlingBot

Signed-off-by: stirlingbot[bot] <1113334+stirlingbot[bot]@users.noreply.github.com>
Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-02-22 22:46:49 +00:00
Peter Dave Hello
ac09831654 Update zh_TW Traditional Chinese translation (#3020)
# Description of Changes

Update zh_TW Traditional Chinese translation

---

## Checklist

### General

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [x] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-22 13:11:59 +00:00
dependabot[bot]
487a82eb65 Bump org.springframework.boot from 3.4.1 to 3.4.3 (#3022)
Bumps
[org.springframework.boot](https://github.com/spring-projects/spring-boot)
from 3.4.1 to 3.4.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-boot/releases">org.springframework.boot's
releases</a>.</em></p>
<blockquote>
<h2>v3.4.3</h2>
<h2> New Features</h2>
<ul>
<li>Add TWENTY_FOUR to JavaVersion enum <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44209">#44209</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Console output may be lost when using Log4j2 with something that
replaces System.out <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44380">#44380</a></li>
<li>Maven plugin does not consistently use ArgFile for classpath
argument on Windows <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44328">#44328</a></li>
<li>Reactive Jetty web server does not fail fast when configured to use
a server name bundle which Jetty does not support <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44319">#44319</a></li>
<li>When web server application context refresh fails, the original
failure is lost if stopping or destroying the web server throws an
exception <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44317">#44317</a></li>
<li>View resolver for Thymeleaf should back off if spring-webmvc is not
present <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44296">#44296</a></li>
<li>WebServer is not destroyed when ReactiveWebServerApplicationContext
refresh fails <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44294">#44294</a></li>
<li>Non-default DataSource candidates are not considered in
H2ConsoleAutoConfiguration <a
href="https://redirect.github.com/spring-projects/spring-boot/pull/44293">#44293</a></li>
<li>Banner placeholder and defaults do not work during development <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44255">#44255</a></li>
<li>Mustache templates return with ISO-8859-1 charset rather than UTF-8
in Content-Type response header <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44193">#44193</a></li>
<li>Servlet EndpointRequest doesn't match web server namespace correctly
<a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44188">#44188</a></li>
<li>java.lang.ClassCastException when using default management security
with WebFlux and health probes enabled <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44052">#44052</a></li>
<li>Logback configuration that relies on inner-classes does not work in
a native image <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44025">#44025</a></li>
<li>IllegalStateException: Unable to register SSL bundle after 3.3.8 or
3.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43989">#43989</a></li>
<li>Metrics and health do not include non-default candidate beans <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43481">#43481</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Document that auto-configuration classes should be identified using
their binary names <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44303">#44303</a></li>
<li>Correct typo in MVC security when explaining when UserDetailsService
auto-configuration will back off <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44301">#44301</a></li>
<li>Link to JarLauncher's javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44170">#44170</a></li>
<li>When using observability annotations, recommend that care is taken
to avoid double instrumentation <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44145">#44145</a></li>
<li>Fix typo in Running Your Application <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44035">#44035</a></li>
<li>Document Kubernetes preStop handler when using a Docker image
without a shell <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44022">#44022</a></li>
<li>Source snippet in Developing Your First Spring Boot Application
section uses the root package <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43983">#43983</a></li>
<li>Correct the location of MyApplication.java in &quot;Developing Your
First Spring Boot Application&quot; <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43975">#43975</a></li>
<li>Add links to Jackson Javadoc <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43971">#43971</a></li>
<li>Warn that some Quartz database schema scripts must be modified
before use <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/43958">#43958</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Upgrade to Commons Pool2 2.12.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44173">#44173</a></li>
<li>Upgrade to Couchbase Client 3.7.8 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44269">#44269</a></li>
<li>Upgrade to Groovy 4.0.25 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44174">#44174</a></li>
<li>Upgrade to Hibernate 6.6.8.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44332">#44332</a></li>
<li>Upgrade to HttpClient5 5.4.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44176">#44176</a></li>
<li>Upgrade to HttpCore5 5.3.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44177">#44177</a></li>
<li>Upgrade to Infinispan 15.0.13.Final <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44178">#44178</a></li>
<li>Upgrade to jOOQ 3.19.19 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44368">#44368</a></li>
<li>Upgrade to Json-smart 2.5.2 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44264">#44264</a></li>
<li>Upgrade to Maven Clean Plugin 3.4.1 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44349">#44349</a></li>
<li>Upgrade to Micrometer 1.14.4 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44115">#44115</a></li>
<li>Upgrade to Micrometer Tracing 1.4.3 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44116">#44116</a></li>
<li>Upgrade to Native Build Tools Plugin 0.10.5 <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44179">#44179</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2f53c0abc0"><code>2f53c0a</code></a>
Release v3.4.3</li>
<li><a
href="f99171fe2e"><code>f99171f</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="70e0744c27"><code>70e0744</code></a>
Next development version (v3.3.10-SNAPSHOT)</li>
<li><a
href="07d9db3b80"><code>07d9db3</code></a>
Merge pull request <a
href="https://redirect.github.com/spring-projects/spring-boot/issues/44380">#44380</a>
from nosan</li>
<li><a
href="22958097e3"><code>2295809</code></a>
Register Log42J StatusListener</li>
<li><a
href="575655ca39"><code>575655c</code></a>
Upgrade Tomcat 11 smoke tests to Tomcat 11.0.4</li>
<li><a
href="c74397af1a"><code>c74397a</code></a>
Merge branch '3.3.x' into 3.4.x</li>
<li><a
href="c718461450"><code>c718461</code></a>
Protect against NoSuchMethodException on setReadOnly</li>
<li><a
href="7dc9bf29b8"><code>7dc9bf2</code></a>
Upgrade to Testcontainers Redis Module 2.2.4</li>
<li><a
href="7d1fc062bf"><code>7d1fc06</code></a>
Upgrade to Testcontainers 1.20.5</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.springframework.boot&package-manager=gradle&previous-version=3.4.1&new-version=3.4.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-22 13:11:35 +00:00
Ludy
0f5db746c7 Bump me.friwi:jcefmaven: from 127.3.1 to 132.3.1 (#3023)
# Description of Changes

see: https://github.com/jcefmaven/jcefmaven/issues/119

---

## Checklist

### General

- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-02-22 12:17:35 +00:00
486 changed files with 1900 additions and 131736 deletions

View File

@@ -121,7 +121,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_API }} password: ${{ secrets.DOCKER_HUB_API }}
- name: Build and push PR-specific image - name: Build and push PR-specific image
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile

View File

@@ -49,7 +49,7 @@ jobs:
- name: Upload Test Reports - name: Upload Test Reports
if: always() if: always()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
name: test-reports-jdk-${{ matrix.jdk-version }} name: test-reports-jdk-${{ matrix.jdk-version }}
path: | path: |
@@ -80,7 +80,7 @@ jobs:
- name: FAILED - check the licenses for compatibility - name: FAILED - check the licenses for compatibility
if: failure() if: failure()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
name: dependencies-without-allowed-license.json name: dependencies-without-allowed-license.json
path: | path: |

View File

@@ -24,7 +24,7 @@ jobs:
- name: Generate GitHub App Token - name: Generate GitHub App Token
id: generate-token id: generate-token
uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v1.11.3 uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5
with: with:
app-id: ${{ secrets.GH_APP_ID }} app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
@@ -45,7 +45,7 @@ jobs:
- name: FAILED - check the licenses for compatibility - name: FAILED - check the licenses for compatibility
if: failure() if: failure()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
name: dependencies-without-allowed-license.json name: dependencies-without-allowed-license.json
path: | path: |

View File

@@ -80,7 +80,7 @@ jobs:
mv ./build/libs/Stirling-PDF-${{ needs.read_versions.outputs.version }}.jar ./binaries/Stirling-PDF${{ matrix.file_suffix }}.jar mv ./build/libs/Stirling-PDF-${{ needs.read_versions.outputs.version }}.jar ./binaries/Stirling-PDF${{ matrix.file_suffix }}.jar
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
retention-days: 1 retention-days: 1
if-no-files-found: error if-no-files-found: error
@@ -114,7 +114,7 @@ jobs:
run: ls -R run: ls -R
- name: Upload signed artifacts - name: Upload signed artifacts
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
retention-days: 1 retention-days: 1
if-no-files-found: error if-no-files-found: error
@@ -188,7 +188,7 @@ jobs:
run: ls -R ./binaries run: ls -R ./binaries
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
retention-days: 1 retention-days: 1
if-no-files-found: error if-no-files-found: error
@@ -224,7 +224,7 @@ jobs:
- name: Install Cosign - name: Install Cosign
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
uses: sigstore/cosign-installer@c56c2d3e59e4281cc41dea2217323ba5694b171e # v3.8.0 uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
- name: Generate key pair - name: Generate key pair
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
@@ -255,7 +255,7 @@ jobs:
run: ls -R run: ls -R
- name: Upload signed artifacts - name: Upload signed artifacts
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
retention-days: 1 retention-days: 1
if-no-files-found: error if-no-files-found: error

View File

@@ -22,7 +22,7 @@ jobs:
- name: Generate GitHub App Token - name: Generate GitHub App Token
id: generate-token id: generate-token
uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v1.11.3 uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5
with: with:
app-id: ${{ secrets.GH_APP_ID }} app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}

View File

@@ -42,7 +42,7 @@ jobs:
- name: Install cosign - name: Install cosign
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master'
uses: sigstore/cosign-installer@c56c2d3e59e4281cc41dea2217323ba5694b171e # v3.8.0 uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
with: with:
cosign-release: "v2.4.1" cosign-release: "v2.4.1"
@@ -90,7 +90,7 @@ jobs:
- name: Build and push main Dockerfile - name: Build and push main Dockerfile
id: build-push-regular id: build-push-regular
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
with: with:
builder: ${{ steps.buildx.outputs.name }} builder: ${{ steps.buildx.outputs.name }}
context: . context: .
@@ -135,7 +135,7 @@ jobs:
- name: Build and push Dockerfile-ultra-lite - name: Build and push Dockerfile-ultra-lite
id: build-push-lite id: build-push-lite
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
if: github.ref != 'refs/heads/main' if: github.ref != 'refs/heads/main'
with: with:
context: . context: .
@@ -166,7 +166,7 @@ jobs:
- name: Build and push main Dockerfile fat - name: Build and push main Dockerfile fat
id: build-push-fat id: build-push-fat
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
if: github.ref != 'refs/heads/main' if: github.ref != 'refs/heads/main'
with: with:
builder: ${{ steps.buildx.outputs.name }} builder: ${{ steps.buildx.outputs.name }}

View File

@@ -63,7 +63,7 @@ jobs:
ls -R ./build/launch4j ls -R ./build/launch4j
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
name: binaries${{ matrix.file_suffix }} name: binaries${{ matrix.file_suffix }}
path: | path: |
@@ -95,7 +95,7 @@ jobs:
run: ls -R run: ls -R
- name: Install Cosign - name: Install Cosign
uses: sigstore/cosign-installer@c56c2d3e59e4281cc41dea2217323ba5694b171e # v3.8.0 uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
- name: Generate key pair - name: Generate key pair
run: cosign generate-key-pair run: cosign generate-key-pair
@@ -139,7 +139,7 @@ jobs:
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe ./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
- name: Upload signed artifacts - name: Upload signed artifacts
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
name: signed${{ matrix.file_suffix }} name: signed${{ matrix.file_suffix }}
path: | path: |

View File

@@ -44,7 +44,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: "Run analysis" - name: "Run analysis"
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
with: with:
results_file: results.sarif results_file: results.sarif
results_format: sarif results_format: sarif
@@ -66,7 +66,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab. # format to the repository Actions tab.
- name: "Upload artifact" - name: "Upload artifact"
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
name: SARIF file name: SARIF file
path: results.sarif path: results.sarif
@@ -74,6 +74,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard. # Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning" - name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
with: with:
sarif_file: results.sarif sarif_file: results.sarif

View File

@@ -46,7 +46,7 @@ jobs:
- name: Upload Problems Report on Failure - name: Upload Problems Report on Failure
if: failure() if: failure()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
name: gradle-problems-report name: gradle-problems-report
path: build/reports/problems/problems-report.html path: build/reports/problems/problems-report.html
@@ -54,7 +54,7 @@ jobs:
- name: Upload Sonar Logs on Failure - name: Upload Sonar Logs on Failure
if: failure() if: failure()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with: with:
name: sonar-logs name: sonar-logs
path: | path: |

View File

@@ -30,7 +30,7 @@ jobs:
- name: Generate GitHub App Token - name: Generate GitHub App Token
id: generate-token id: generate-token
uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v1.11.3 uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5
with: with:
app-id: ${{ secrets.GH_APP_ID }} app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
@@ -63,7 +63,7 @@ jobs:
- name: Generate GitHub App Token - name: Generate GitHub App Token
id: generate-token id: generate-token
uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v1.11.3 uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5
with: with:
app-id: ${{ vars.GH_APP_ID }} app-id: ${{ vars.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}

View File

@@ -46,7 +46,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_API }} password: ${{ secrets.DOCKER_HUB_API }}
- name: Build and push test image - name: Build and push test image
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile

View File

@@ -9,8 +9,8 @@ COPY . .
# Build the application with DOCKER_ENABLE_SECURITY=false # Build the application with DOCKER_ENABLE_SECURITY=false
RUN DOCKER_ENABLE_SECURITY=true \ RUN DOCKER_ENABLE_SECURITY=true \
STIRLING_PDF_DESKTOP_UI=false \ STIRLING_PDF_DESKTOP_UI=false \
./gradlew clean build ./gradlew clean build
# Main stage # Main stage
FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
@@ -50,29 +50,29 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \ echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
apk upgrade --no-cache -a && \ apk upgrade --no-cache -a && \
apk add --no-cache \ apk add --no-cache \
ca-certificates \ ca-certificates \
tzdata \ tzdata \
tini \ tini \
bash \ bash \
curl \ curl \
shadow \ shadow \
su-exec \ su-exec \
openssl \ openssl \
openssl-dev \ openssl-dev \
openjdk21-jre \ openjdk21-jre \
# Doc conversion # Doc conversion
gcompat \ gcompat \
libc6-compat \ libc6-compat \
libreoffice \ libreoffice \
# pdftohtml # pdftohtml
poppler-utils \ poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced featues) # OCR MY PDF (unpaper for descew and other advanced featues)
qpdf \ qpdf \
tesseract-ocr-data-eng \ tesseract-ocr-data-eng \
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra \ font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra font-liberation font-linux-libertine \
# CV # CV
py3-opencv \ py3-opencv \
python3 \ python3 \
py3-pip \ py3-pip \
py3-pillow@testing \ py3-pillow@testing \
@@ -89,7 +89,7 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
fc-cache -f -v && \ fc-cache -f -v && \
chmod +x /scripts/* && \ chmod +x /scripts/* && \
chmod +x /scripts/init.sh && \ chmod +x /scripts/init.sh && \
# User permissions # User permissions
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \ addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \ chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
chown stirlingpdfuser:stirlingpdfgroup /app.jar chown stirlingpdfuser:stirlingpdfgroup /app.jar
@@ -97,4 +97,4 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
EXPOSE 8080/tcp EXPOSE 8080/tcp
# Set user and run command # Set user and run command
ENTRYPOINT ["tini", "--", "/scripts/init.sh"] ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 0.0.0.0"] CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 0.0.0.0"]

View File

@@ -148,11 +148,11 @@ Stirling-PDF currently supports 39 languages!
| Simplified Chinese (简体中文) (zh_CN) | ![99%](https://geps.dev/progress/99) | | Simplified Chinese (简体中文) (zh_CN) | ![99%](https://geps.dev/progress/99) |
| Slovakian (Slovensky) (sk_SK) | ![74%](https://geps.dev/progress/74) | | Slovakian (Slovensky) (sk_SK) | ![74%](https://geps.dev/progress/74) |
| Slovenian (Slovenščina) (sl_SI) | ![96%](https://geps.dev/progress/96) | | Slovenian (Slovenščina) (sl_SI) | ![96%](https://geps.dev/progress/96) |
| Spanish (Español) (es_ES) | ![86%](https://geps.dev/progress/86) | | Spanish (Español) (es_ES) | ![98%](https://geps.dev/progress/98) |
| Swedish (Svenska) (sv_SE) | ![92%](https://geps.dev/progress/92) | | Swedish (Svenska) (sv_SE) | ![92%](https://geps.dev/progress/92) |
| Thai (ไทย) (th_TH) | ![85%](https://geps.dev/progress/85) | | Thai (ไทย) (th_TH) | ![85%](https://geps.dev/progress/85) |
| Tibetan (བོད་ཡིག་) (zh_BO) | ![94%](https://geps.dev/progress/94) | | Tibetan (བོད་ཡིག་) (zh_BO) | ![94%](https://geps.dev/progress/94) |
| Traditional Chinese (繁體中文) (zh_TW) | ![98%](https://geps.dev/progress/98) | | Traditional Chinese (繁體中文) (zh_TW) | ![99%](https://geps.dev/progress/99) |
| Turkish (Türkçe) (tr_TR) | ![82%](https://geps.dev/progress/82) | | Turkish (Türkçe) (tr_TR) | ![82%](https://geps.dev/progress/82) |
| Ukrainian (Українська) (uk_UA) | ![72%](https://geps.dev/progress/72) | | Ukrainian (Українська) (uk_UA) | ![72%](https://geps.dev/progress/72) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![79%](https://geps.dev/progress/79) | | Vietnamese (Tiếng Việt) (vi_VN) | ![79%](https://geps.dev/progress/79) |

View File

@@ -1,6 +1,6 @@
plugins { plugins {
id "java" id "java"
id "org.springframework.boot" version "3.4.1" id "org.springframework.boot" version "3.4.3"
id "io.spring.dependency-management" version "1.1.7" id "io.spring.dependency-management" version "1.1.7"
id "org.springdoc.openapi-gradle-plugin" version "1.8.0" id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
id "io.swagger.swaggerhub" version "1.3.2" id "io.swagger.swaggerhub" version "1.3.2"
@@ -15,17 +15,17 @@ plugins {
import com.github.jk1.license.render.* import com.github.jk1.license.render.*
ext { ext {
springBootVersion = "3.4.2" springBootVersion = "3.4.3"
pdfboxVersion = "3.0.4" pdfboxVersion = "3.0.4"
imageioVersion = "3.12.0" imageioVersion = "3.12.0"
lombokVersion = "1.18.36" lombokVersion = "1.18.36"
bouncycastleVersion = "1.80" bouncycastleVersion = "1.80"
springSecuritySamlVersion = "6.4.2" springSecuritySamlVersion = "6.4.3"
openSamlVersion = "4.3.2" openSamlVersion = "4.3.2"
} }
group = "stirling.software" group = "stirling.software"
version = "0.42.0" version = "0.43.0"
java { java {
// 17 is lowest but we support and recommend 21 // 17 is lowest but we support and recommend 21
@@ -294,8 +294,8 @@ configurations.all {
dependencies { dependencies {
//tmp for security bumps //tmp for security bumps
implementation 'ch.qos.logback:logback-core:1.5.15' implementation 'ch.qos.logback:logback-core:1.5.16'
implementation 'ch.qos.logback:logback-classic:1.5.15' implementation 'ch.qos.logback:logback-classic:1.5.16'
// Exclude vulnerable BouncyCastle version used in tableau // Exclude vulnerable BouncyCastle version used in tableau
@@ -306,8 +306,7 @@ dependencies {
} }
if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") { if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") {
implementation 'org.apache.commons:commons-compress:1.26.0' implementation "me.friwi:jcefmaven:132.3.1"
implementation "me.friwi:jcefmaven:127.3.1"
implementation "org.openjfx:javafx-controls:21" implementation "org.openjfx:javafx-controls:21"
implementation "org.openjfx:javafx-swing:21" implementation "org.openjfx:javafx-swing:21"
} }
@@ -332,7 +331,7 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion" implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
implementation "org.springframework.session:spring-session-core:3.4.1" implementation "org.springframework.session:spring-session-core:3.4.2"
implementation "org.springframework:spring-jdbc:6.2.3" implementation "org.springframework:spring-jdbc:6.2.3"
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5' implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'

View File

@@ -20,6 +20,7 @@ import stirling.software.SPDF.utils.GeneralUtils;
@Service @Service
@Slf4j @Slf4j
public class KeygenLicenseVerifier { public class KeygenLicenseVerifier {
// todo: place in config files?
private static final String ACCOUNT_ID = "e5430f69-e834-4ae4-befd-b602aae5f372"; private static final String ACCOUNT_ID = "e5430f69-e834-4ae4-befd-b602aae5f372";
private static final String BASE_URL = "https://api.keygen.sh/v1/accounts"; private static final String BASE_URL = "https://api.keygen.sh/v1/accounts";
private static final ObjectMapper objectMapper = new ObjectMapper(); private static final ObjectMapper objectMapper = new ObjectMapper();
@@ -68,7 +69,7 @@ public class KeygenLicenseVerifier {
return false; return false;
} catch (Exception e) { } catch (Exception e) {
log.error("Error verifying license: " + e.getMessage()); log.error("Error verifying license: {}", e.getMessage());
return false; return false;
} }
} }
@@ -95,10 +96,9 @@ public class KeygenLicenseVerifier {
.build(); .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
log.debug(" validateLicenseResponse body: " + response.body()); log.debug("ValidateLicenseResponse body: {}", response.body());
JsonNode jsonResponse = objectMapper.readTree(response.body()); JsonNode jsonResponse = objectMapper.readTree(response.body());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
JsonNode metaNode = jsonResponse.path("meta"); JsonNode metaNode = jsonResponse.path("meta");
boolean isValid = metaNode.path("valid").asBoolean(); boolean isValid = metaNode.path("valid").asBoolean();
@@ -120,7 +120,7 @@ public class KeygenLicenseVerifier {
log.info(applicationProperties.toString()); log.info(applicationProperties.toString());
} else { } else {
log.error("Error validating license. Status code: " + response.statusCode()); log.error("Error validating license. Status code: {}", response.statusCode());
} }
return jsonResponse; return jsonResponse;
} }

View File

@@ -1,7 +1,6 @@
package stirling.software.SPDF; package stirling.software.SPDF;
import java.io.IOException; import java.io.IOException;
import java.net.ServerSocket;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@@ -29,6 +28,7 @@ import stirling.software.SPDF.UI.WebBrowser;
import stirling.software.SPDF.config.ConfigInitializer; import stirling.software.SPDF.config.ConfigInitializer;
import stirling.software.SPDF.config.InstallationPathConfig; import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.utils.UrlUtils;
@Slf4j @Slf4j
@EnableScheduling @EnableScheduling
@@ -64,6 +64,12 @@ public class SPDFApplication {
app.setHeadless(false); app.setHeadless(false);
props.put("java.awt.headless", "false"); props.put("java.awt.headless", "false");
props.put("spring.main.web-application-type", "servlet"); props.put("spring.main.web-application-type", "servlet");
int desiredPort = 8080;
String port = UrlUtils.findAvailablePort(desiredPort);
props.put("server.port", port);
System.setProperty("server.port", port);
log.info("Desktop UI mode: Using port {}", port);
} }
app.setAdditionalProfiles(getActiveProfile(args)); app.setAdditionalProfiles(getActiveProfile(args));
@@ -160,7 +166,17 @@ public class SPDFApplication {
} }
@Value("${server.port:8080}") @Value("${server.port:8080}")
public void setServerPortStatic(String port) { public void setServerPort(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;
}
}
public static void setServerPortStatic(String port) {
if ("auto".equalsIgnoreCase(port)) { if ("auto".equalsIgnoreCase(port)) {
// Use Spring Boot's automatic port assignment (server.port=0) // Use Spring Boot's automatic port assignment (server.port=0)
SPDFApplication.serverPortStatic = SPDFApplication.serverPortStatic =
@@ -197,36 +213,11 @@ public class SPDFApplication {
return new String[] {"default"}; 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() { public static String getStaticBaseUrl() {
return baseUrlStatic; return baseUrlStatic;
} }
public String getNonStaticBaseUrl() {
return baseUrlStatic;
}
public static String getStaticPort() { public static String getStaticPort() {
return serverPortStatic; return serverPortStatic;
} }
public String getNonStaticPort() {
return serverPortStatic;
}
} }

View File

@@ -35,10 +35,7 @@ public class AppConfig {
} }
@Bean @Bean
@ConditionalOnProperty( @ConditionalOnProperty(name = "system.customHTMLFiles", havingValue = "true")
name = "system.customHTMLFiles",
havingValue = "true",
matchIfMissing = false)
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) { public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine(); SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(new FileFallbackTemplateResolver(resourceLoader)); templateEngine.addTemplateResolver(new FileFallbackTemplateResolver(resourceLoader));
@@ -129,8 +126,8 @@ public class AppConfig {
} }
@ConditionalOnMissingClass("stirling.software.SPDF.config.security.SecurityConfiguration") @ConditionalOnMissingClass("stirling.software.SPDF.config.security.SecurityConfiguration")
@Bean(name = "activSecurity") @Bean(name = "activeSecurity")
public boolean missingActivSecurity() { public boolean missingActiveSecurity() {
return false; return false;
} }

View File

@@ -2,13 +2,13 @@ package stirling.software.SPDF.config;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import stirling.software.SPDF.config.interfaces.ShowAdminInterface; import stirling.software.SPDF.config.interfaces.ShowAdminInterface;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
@Service @Configuration
class AppUpdateService { class AppUpdateService {
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;

View File

@@ -20,7 +20,7 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
"endpoints", "endpoints",
"logout", "logout",
"error", "error",
"erroroauth", "errorOAuth",
"file", "file",
"messageType", "messageType",
"infoMessage"); "infoMessage");

View File

@@ -57,12 +57,11 @@ public class RuntimePathConfig {
this.pipelineDefaultWebUiConfigs = webUiConfigsPath; this.pipelineDefaultWebUiConfigs = webUiConfigsPath;
boolean isDocker = isRunningInDocker(); boolean isDocker = isRunningInDocker();
// Initialize Operation paths // Initialize Operation paths
String weasyPrintPath = isDocker ? "/opt/venv/bin/weasyprint" : "weasyprint"; String weasyPrintPath = isDocker ? "/opt/venv/bin/weasyprint" : "weasyprint";
String unoConvertPath = isDocker ? "/opt/venv/bin/unoconvert" : "unoconvert"; String unoConvertPath = isDocker ? "/opt/venv/bin/unoconvert" : "unoconvert";
// Check for custom operation paths // Check for custom operation paths
Operations operations = properties.getSystem().getCustomPaths().getOperations(); Operations operations = properties.getSystem().getCustomPaths().getOperations();
if (operations != null) { if (operations != null) {
@@ -78,9 +77,8 @@ public class RuntimePathConfig {
this.weasyPrintPath = weasyPrintPath; this.weasyPrintPath = weasyPrintPath;
this.unoConvertPath = unoConvertPath; this.unoConvertPath = unoConvertPath;
} }
private boolean isRunningInDocker() { private boolean isRunningInDocker() {
return Files.exists(Paths.get("/.dockerenv")); return Files.exists(Paths.get("/.dockerenv"));
} }
} }

View File

@@ -3,7 +3,7 @@ package stirling.software.SPDF.config.interfaces;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import stirling.software.SPDF.model.provider.UnsupportedProviderException; import stirling.software.SPDF.model.exception.UnsupportedProviderException;
import stirling.software.SPDF.utils.FileInfo; import stirling.software.SPDF.utils.FileInfo;
public interface DatabaseInterface { public interface DatabaseInterface {

View File

@@ -69,7 +69,7 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
} }
if (exception instanceof BadCredentialsException if (exception instanceof BadCredentialsException
|| exception instanceof UsernameNotFoundException) { || exception instanceof UsernameNotFoundException) {
getRedirectStrategy().sendRedirect(request, response, "/login?error=badcredentials"); getRedirectStrategy().sendRedirect(request, response, "/login?error=badCredentials");
return; return;
} }
if (exception instanceof InternalAuthenticationServiceException if (exception instanceof InternalAuthenticationServiceException

View File

@@ -14,8 +14,8 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2A
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import com.coveo.saml.SamlClient; import com.coveo.saml.SamlClient;
import com.coveo.saml.SamlException;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
@@ -28,62 +28,43 @@ import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrin
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2; import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2; import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
import stirling.software.SPDF.model.Provider; import stirling.software.SPDF.model.provider.KeycloakProvider;
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
import stirling.software.SPDF.utils.UrlUtils; import stirling.software.SPDF.utils.UrlUtils;
@Slf4j @Slf4j
@AllArgsConstructor @AllArgsConstructor
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
public static final String LOGOUT_PATH = "/login?logout=true";
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
@Override @Override
public void onLogoutSuccess( public void onLogoutSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication) HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException { throws IOException {
if (!response.isCommitted()) { if (!response.isCommitted()) {
// Handle user logout due to disabled account
if (request.getParameter("userIsDisabled") != null) {
response.sendRedirect(
request.getContextPath() + "/login?erroroauth=userIsDisabled");
return;
}
// Handle OAuth2 authentication error
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
response.sendRedirect(
request.getContextPath() + "/login?erroroauth=userAlreadyExistsWeb");
return;
}
if (authentication != null) { if (authentication != null) {
// Handle SAML2 logout redirection
if (authentication instanceof Saml2Authentication) { if (authentication instanceof Saml2Authentication) {
// Handle SAML2 logout redirection
getRedirect_saml2(request, response, authentication); getRedirect_saml2(request, response, authentication);
return; } else if (authentication instanceof OAuth2AuthenticationToken) {
} // Handle OAuth2 logout redirection
// Handle OAuth2 logout redirection
else if (authentication instanceof OAuth2AuthenticationToken) {
getRedirect_oauth2(request, response, authentication); getRedirect_oauth2(request, response, authentication);
return; } else if (authentication instanceof UsernamePasswordAuthenticationToken) {
} // Handle Username/Password logout
// Handle Username/Password logout getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
else if (authentication instanceof UsernamePasswordAuthenticationToken) { } else {
getRedirectStrategy().sendRedirect(request, response, "/login?logout=true"); // Handle unknown authentication types
return;
}
// Handle unknown authentication types
else {
log.error( log.error(
"authentication class unknown: " "Authentication class unknown: {}",
+ authentication.getClass().getSimpleName()); authentication.getClass().getSimpleName());
getRedirectStrategy().sendRedirect(request, response, "/login?logout=true"); getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
return;
} }
} else { } else {
// Redirect to login page after logout // Redirect to login page after logout
getRedirectStrategy().sendRedirect(request, response, "/login?logout=true"); String path = checkForErrors(request);
return; getRedirectStrategy().sendRedirect(request, response, path);
} }
} }
} }
@@ -100,7 +81,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
CustomSaml2AuthenticatedPrincipal principal = CustomSaml2AuthenticatedPrincipal principal =
(CustomSaml2AuthenticatedPrincipal) samlAuthentication.getPrincipal(); (CustomSaml2AuthenticatedPrincipal) samlAuthentication.getPrincipal();
String nameIdValue = principal.getName(); String nameIdValue = principal.name();
try { try {
// Read certificate from the resource // Read certificate from the resource
@@ -111,27 +92,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
certificates.add(certificate); certificates.add(certificate);
// Construct URLs required for SAML configuration // Construct URLs required for SAML configuration
String serverUrl = SamlClient samlClient = getSamlClient(registrationId, samlConf, certificates);
SPDFApplication.getStaticBaseUrl() + ":" + SPDFApplication.getStaticPort();
String relyingPartyIdentifier =
serverUrl + "/saml2/service-provider-metadata/" + registrationId;
String assertionConsumerServiceUrl = serverUrl + "/login/saml2/sso/" + registrationId;
String idpUrl = samlConf.getIdpSingleLogoutUrl();
String idpIssuer = samlConf.getIdpIssuer();
// Create SamlClient instance for SAML logout
SamlClient samlClient =
new SamlClient(
relyingPartyIdentifier,
assertionConsumerServiceUrl,
idpUrl,
idpIssuer,
certificates,
SamlClient.SamlIdpBinding.POST);
// Read private key for service provider // Read private key for service provider
Resource privateKeyResource = samlConf.getPrivateKey(); Resource privateKeyResource = samlConf.getPrivateKey();
@@ -143,8 +104,12 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
// Redirect to identity provider for logout // Redirect to identity provider for logout
samlClient.redirectToIdentityProvider(response, null, nameIdValue); samlClient.redirectToIdentityProvider(response, null, nameIdValue);
} catch (Exception e) { } catch (Exception e) {
log.error(nameIdValue, e); log.error(
getRedirectStrategy().sendRedirect(request, response, "/login?logout=true"); "Error retrieving logout URL from Provider {} for user {}",
samlConf.getProvider(),
nameIdValue,
e);
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
} }
} }
@@ -152,87 +117,107 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
private void getRedirect_oauth2( private void getRedirect_oauth2(
HttpServletRequest request, HttpServletResponse response, Authentication authentication) HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException { throws IOException {
String param = "logout=true"; String registrationId;
String registrationId = null;
String issuer = null;
String clientId = null;
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2(); OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
String path = checkForErrors(request);
if (authentication instanceof OAuth2AuthenticationToken) { if (authentication instanceof OAuth2AuthenticationToken oauthToken) {
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
registrationId = oauthToken.getAuthorizedClientRegistrationId(); registrationId = oauthToken.getAuthorizedClientRegistrationId();
try {
// Get OAuth2 provider details from configuration
Provider provider = oauth.getClient().get(registrationId);
issuer = provider.getIssuer();
clientId = provider.getClientId();
} catch (UnsupportedProviderException e) {
log.error(e.getMessage());
}
} else { } else {
registrationId = oauth.getProvider() != null ? oauth.getProvider() : ""; registrationId = oauth.getProvider() != null ? oauth.getProvider() : "";
issuer = oauth.getIssuer();
clientId = oauth.getClientId();
}
String errorMessage = "";
// Handle different error scenarios during logout
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
param = "erroroauth=oauth2AuthenticationErrorWeb";
} else if ((errorMessage = request.getParameter("error")) != null) {
param = "error=" + sanitizeInput(errorMessage);
} else if ((errorMessage = request.getParameter("erroroauth")) != null) {
param = "erroroauth=" + sanitizeInput(errorMessage);
} else if (request.getParameter("oauth2AutoCreateDisabled") != null) {
param = "error=oauth2AutoCreateDisabled";
} else if (request.getParameter("oauth2_admin_blocked_user") != null) {
param = "erroroauth=oauth2_admin_blocked_user";
} else if (request.getParameter("userIsDisabled") != null) {
param = "erroroauth=userIsDisabled";
} else if (request.getParameter("badcredentials") != null) {
param = "error=badcredentials";
} }
String redirect_url = UrlUtils.getOrigin(request) + "/login?" + param; String redirectUrl = UrlUtils.getOrigin(request) + "/login?" + path;
// Redirect based on OAuth2 provider // Redirect based on OAuth2 provider
switch (registrationId.toLowerCase()) { switch (registrationId.toLowerCase()) {
case "keycloak": case "keycloak" -> {
// Add Keycloak specific logout URL if needed KeycloakProvider keycloak = oauth.getClient().getKeycloak();
String logoutUrl = String logoutUrl =
issuer keycloak.getIssuer()
+ "/protocol/openid-connect/logout" + "/protocol/openid-connect/logout"
+ "?client_id=" + "?client_id="
+ clientId + keycloak.getClientId()
+ "&post_logout_redirect_uri=" + "&post_logout_redirect_uri="
+ response.encodeRedirectURL(redirect_url); + response.encodeRedirectURL(redirectUrl);
log.info("Redirecting to Keycloak logout URL: " + logoutUrl); log.info("Redirecting to Keycloak logout URL: {}", logoutUrl);
response.sendRedirect(logoutUrl); response.sendRedirect(logoutUrl);
break; }
case "github": case "github", "google" -> {
// Add GitHub specific logout URL if needed log.info(
String githubLogoutUrl = "https://github.com/logout"; "No redirect URL for {} available. Redirecting to default logout URL: {}",
log.info("Redirecting to GitHub logout URL: " + githubLogoutUrl); registrationId,
response.sendRedirect(githubLogoutUrl); redirectUrl);
break; response.sendRedirect(redirectUrl);
case "google": }
// Add Google specific logout URL if needed default -> {
// String googleLogoutUrl = log.info("Redirecting to default logout URL: {}", redirectUrl);
// "https://accounts.google.com/Logout?continue=https://appengine.google.com/_ah/logout?continue=" response.sendRedirect(redirectUrl);
// + response.encodeRedirectURL(redirect_url); }
log.info("Google does not have a specific logout URL");
// log.info("Redirecting to Google logout URL: " + googleLogoutUrl);
// response.sendRedirect(googleLogoutUrl);
// break;
default:
String defaultRedirectUrl = request.getContextPath() + "/login?" + param;
log.info("Redirecting to default logout URL: " + defaultRedirectUrl);
response.sendRedirect(defaultRedirectUrl);
break;
} }
} }
// Sanitize input to avoid potential security vulnerabilities private static SamlClient getSamlClient(
String registrationId, SAML2 samlConf, List<X509Certificate> certificates)
throws SamlException {
String serverUrl =
SPDFApplication.getStaticBaseUrl() + ":" + SPDFApplication.getStaticPort();
String relyingPartyIdentifier =
serverUrl + "/saml2/service-provider-metadata/" + registrationId;
String assertionConsumerServiceUrl = serverUrl + "/login/saml2/sso/" + registrationId;
String idpSLOUrl = samlConf.getIdpSingleLogoutUrl();
String idpIssuer = samlConf.getIdpIssuer();
// Create SamlClient instance for SAML logout
return new SamlClient(
relyingPartyIdentifier,
assertionConsumerServiceUrl,
idpSLOUrl,
idpIssuer,
certificates,
SamlClient.SamlIdpBinding.POST);
}
/**
* Handles different error scenarios during logout. Will return a <code>String</code> containing
* the error request parameter.
*
* @param request the user's <code>HttpServletRequest</code> request.
* @return a <code>String</code> containing the error request parameter.
*/
private String checkForErrors(HttpServletRequest request) {
String errorMessage;
String path = "logout=true";
if (request.getParameter("oAuth2AuthenticationErrorWeb") != null) {
path = "errorOAuth=userAlreadyExistsWeb";
} else if ((errorMessage = request.getParameter("errorOAuth")) != null) {
path = "errorOAuth=" + sanitizeInput(errorMessage);
} else if (request.getParameter("oAuth2AutoCreateDisabled") != null) {
path = "errorOAuth=oAuth2AutoCreateDisabled";
} else if (request.getParameter("oAuth2AdminBlockedUser") != null) {
path = "errorOAuth=oAuth2AdminBlockedUser";
} else if (request.getParameter("userIsDisabled") != null) {
path = "errorOAuth=userIsDisabled";
} else if ((errorMessage = request.getParameter("error")) != null) {
path = "errorOAuth=" + sanitizeInput(errorMessage);
} else if (request.getParameter("badCredentials") != null) {
path = "errorOAuth=badCredentials";
}
return path;
}
/**
* Sanitize input to avoid potential security vulnerabilities. Will return a sanitised <code>
* String</code>.
*
* @return a sanitised <code>String</code>
*/
private String sanitizeInput(String input) { private String sanitizeInput(String input) {
return input.replaceAll("[^a-zA-Z0-9 ]", ""); return input.replaceAll("[^a-zA-Z0-9 ]", "");
} }

View File

@@ -12,7 +12,7 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.interfaces.DatabaseInterface; import stirling.software.SPDF.config.interfaces.DatabaseInterface;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.Role;
import stirling.software.SPDF.model.provider.UnsupportedProviderException; import stirling.software.SPDF.model.exception.UnsupportedProviderException;
@Slf4j @Slf4j
@Component @Component

View File

@@ -1,6 +1,6 @@
package stirling.software.SPDF.config.security; package stirling.software.SPDF.config.security;
import java.util.*; import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@@ -51,11 +51,7 @@ public class SecurityConfiguration {
private final CustomUserDetailsService userDetailsService; private final CustomUserDetailsService userDetailsService;
private final UserService userService; private final UserService userService;
@Qualifier("loginEnabled")
private final boolean loginEnabledValue; private final boolean loginEnabledValue;
@Qualifier("runningEE")
private final boolean runningEE; private final boolean runningEE;
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
@@ -109,6 +105,7 @@ public class SecurityConfiguration {
if (applicationProperties.getSecurity().getCsrfDisabled() || !loginEnabledValue) { if (applicationProperties.getSecurity().getCsrfDisabled() || !loginEnabledValue) {
http.csrf(csrf -> csrf.disable()); http.csrf(csrf -> csrf.disable());
} }
if (loginEnabledValue) { if (loginEnabledValue) {
http.addFilterBefore( http.addFilterBefore(
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
@@ -164,8 +161,7 @@ public class SecurityConfiguration {
.logoutSuccessHandler( .logoutSuccessHandler(
new CustomLogoutSuccessHandler(applicationProperties)) new CustomLogoutSuccessHandler(applicationProperties))
.clearAuthentication(true) .clearAuthentication(true)
.invalidateHttpSession( // Invalidate session .invalidateHttpSession(true)
true)
.deleteCookies("JSESSIONID", "remember-me")); .deleteCookies("JSESSIONID", "remember-me"));
http.rememberMe( http.rememberMe(
rememberMeConfigurer -> // Use the configurator directly rememberMeConfigurer -> // Use the configurator directly
@@ -227,14 +223,14 @@ public class SecurityConfiguration {
.permitAll()); .permitAll());
} }
// Handle OAUTH2 Logins // Handle OAUTH2 Logins
if (applicationProperties.getSecurity().isOauth2Activ()) { if (applicationProperties.getSecurity().isOauth2Active()) {
http.oauth2Login( http.oauth2Login(
oauth2 -> oauth2 ->
oauth2.loginPage("/oauth2") oauth2.loginPage("/oauth2")
. .
/* /*
This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database. This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database.
If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser' If user exists, login proceeds as usual. If user does not exist, then it is auto-created but only if 'OAUTH2AutoCreateUser'
is set as true, else login fails with an error message advising the same. is set as true, else login fails with an error message advising the same.
*/ */
successHandler( successHandler(
@@ -258,8 +254,7 @@ public class SecurityConfiguration {
.permitAll()); .permitAll());
} }
// Handle SAML // Handle SAML
if (applicationProperties.getSecurity().isSaml2Activ()) { if (applicationProperties.getSecurity().isSaml2Active() && runningEE) {
// && runningEE
// Configure the authentication provider // Configure the authentication provider
OpenSaml4AuthenticationProvider authenticationProvider = OpenSaml4AuthenticationProvider authenticationProvider =
new OpenSaml4AuthenticationProvider(); new OpenSaml4AuthenticationProvider();
@@ -284,12 +279,13 @@ public class SecurityConfiguration {
.authenticationRequestResolver( .authenticationRequestResolver(
saml2AuthenticationRequestResolver); saml2AuthenticationRequestResolver);
} catch (Exception e) { } catch (Exception e) {
log.error("Error configuring SAML2 login", e); log.error("Error configuring SAML 2 login", e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}); });
} }
} else { } else {
log.info("SAML 2 login is not enabled. Using default.");
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll()); http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
} }
return http.build(); return http.build();
@@ -315,7 +311,7 @@ public class SecurityConfiguration {
} }
@Bean @Bean
public boolean activSecurity() { public boolean activeSecurity() {
return true; return true;
} }
} }

View File

@@ -88,7 +88,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
// Use API key to authenticate. This requires you to have an authentication // Use API key to authenticate. This requires you to have an authentication
// provider for API keys. // provider for API keys.
Optional<User> user = userService.getUserByApiKey(apiKey); Optional<User> user = userService.getUserByApiKey(apiKey);
if (!user.isPresent()) { if (user.isEmpty()) {
response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("Invalid API Key."); response.getWriter().write("Invalid API Key.");
return; return;
@@ -150,7 +150,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
OAUTH2 oAuth = securityProp.getOauth2(); OAUTH2 oAuth = securityProp.getOauth2();
blockRegistration = oAuth != null && oAuth.getBlockRegistration(); blockRegistration = oAuth != null && oAuth.getBlockRegistration();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) { } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
username = ((CustomSaml2AuthenticatedPrincipal) principal).getName(); username = ((CustomSaml2AuthenticatedPrincipal) principal).name();
loginMethod = LoginMethod.SAML2USER; loginMethod = LoginMethod.SAML2USER;
SAML2 saml2 = securityProp.getSaml2(); SAML2 saml2 = securityProp.getSaml2();
blockRegistration = saml2 != null && saml2.getBlockRegistration(); blockRegistration = saml2 != null && saml2.getBlockRegistration();
@@ -177,7 +177,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
if (blockRegistration && !isUserExists) { if (blockRegistration && !isUserExists) {
log.warn("Blocked registration for OAuth2/SAML user: {}", username); log.warn("Blocked registration for OAuth2/SAML user: {}", username);
response.sendRedirect( response.sendRedirect(
request.getContextPath() + "/logout?oauth2_admin_blocked_user=true"); request.getContextPath() + "/logout?oAuth2AdminBlockedUser=true");
return; return;
} }
@@ -193,7 +193,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
// Redirect to logout if credentials are invalid // Redirect to logout if credentials are invalid
if (!isUserExists && notSsoLogin) { if (!isUserExists && notSsoLogin) {
response.sendRedirect(request.getContextPath() + "/logout?badcredentials=true"); response.sendRedirect(request.getContextPath() + "/logout?badCredentials=true");
return; return;
} }
if (isUserDisabled) { if (isUserDisabled) {

View File

@@ -27,7 +27,7 @@ import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrin
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry; import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface; import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
import stirling.software.SPDF.model.*; import stirling.software.SPDF.model.*;
import stirling.software.SPDF.model.provider.UnsupportedProviderException; import stirling.software.SPDF.model.exception.UnsupportedProviderException;
import stirling.software.SPDF.repository.AuthorityRepository; import stirling.software.SPDF.repository.AuthorityRepository;
import stirling.software.SPDF.repository.UserRepository; import stirling.software.SPDF.repository.UserRepository;
@@ -78,20 +78,18 @@ public class UserService implements UserServiceInterface {
} }
// Handle OAUTH2 login and user auto creation. // Handle OAUTH2 login and user auto creation.
public boolean processSSOPostLogin(String username, boolean autoCreateUser) public void processSSOPostLogin(String username, boolean autoCreateUser)
throws IllegalArgumentException, SQLException, UnsupportedProviderException { throws IllegalArgumentException, SQLException, UnsupportedProviderException {
if (!isUsernameValid(username)) { if (!isUsernameValid(username)) {
return false; return;
} }
Optional<User> existingUser = findByUsernameIgnoreCase(username); Optional<User> existingUser = findByUsernameIgnoreCase(username);
if (existingUser.isPresent()) { if (existingUser.isPresent()) {
return true; return;
} }
if (autoCreateUser) { if (autoCreateUser) {
saveUser(username, AuthenticationType.SSO); saveUser(username, AuthenticationType.SSO);
return true;
} }
return false;
} }
public Authentication getAuthentication(String apiKey) { public Authentication getAuthentication(String apiKey) {
@@ -373,19 +371,16 @@ public class UserService implements UserServiceInterface {
public void invalidateUserSessions(String username) { public void invalidateUserSessions(String username) {
String usernameP = ""; String usernameP = "";
for (Object principal : sessionRegistry.getAllPrincipals()) { for (Object principal : sessionRegistry.getAllPrincipals()) {
for (SessionInformation sessionsInformation : for (SessionInformation sessionsInformation :
sessionRegistry.getAllSessions(principal, false)) { sessionRegistry.getAllSessions(principal, false)) {
if (principal instanceof UserDetails) { if (principal instanceof UserDetails userDetails) {
UserDetails userDetails = (UserDetails) principal;
usernameP = userDetails.getUsername(); usernameP = userDetails.getUsername();
} else if (principal instanceof OAuth2User) { } else if (principal instanceof OAuth2User oAuth2User) {
OAuth2User oAuth2User = (OAuth2User) principal;
usernameP = oAuth2User.getName(); usernameP = oAuth2User.getName();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) { } else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) {
CustomSaml2AuthenticatedPrincipal saml2User = usernameP = saml2User.name();
(CustomSaml2AuthenticatedPrincipal) principal;
usernameP = saml2User.getName();
} else if (principal instanceof String) { } else if (principal instanceof String) {
usernameP = (String) principal; usernameP = (String) principal;
} }
@@ -398,6 +393,7 @@ public class UserService implements UserServiceInterface {
public String getCurrentUsername() { public String getCurrentUsername() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) { if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername(); return ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) { } else if (principal instanceof OAuth2User) {
@@ -405,42 +401,48 @@ public class UserService implements UserServiceInterface {
.getAttribute( .getAttribute(
applicationProperties.getSecurity().getOauth2().getUseAsUsername()); applicationProperties.getSecurity().getOauth2().getUseAsUsername());
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) { } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
return ((CustomSaml2AuthenticatedPrincipal) principal).getName(); return ((CustomSaml2AuthenticatedPrincipal) principal).name();
} else if (principal instanceof String) {
return (String) principal;
} else { } else {
return principal.toString(); return principal.toString();
} }
} }
@Transactional @Transactional
public void syncCustomApiUser(String customApiKey) public void syncCustomApiUser(String customApiKey) {
throws SQLException, UnsupportedProviderException { if (customApiKey == null || customApiKey.trim().isBlank()) {
if (customApiKey == null || customApiKey.trim().length() == 0) {
return; return;
} }
String username = "CUSTOM_API_USER"; String username = "CUSTOM_API_USER";
Optional<User> existingUser = findByUsernameIgnoreCase(username); Optional<User> existingUser = findByUsernameIgnoreCase(username);
if (!existingUser.isPresent()) {
// Create new user with API role existingUser.ifPresentOrElse(
User user = new User(); user -> {
user.setUsername(username); // Update API key if it has changed
user.setPassword(UUID.randomUUID().toString()); User updatedUser = existingUser.get();
user.setEnabled(true);
user.setFirstLogin(false); if (!customApiKey.equals(updatedUser.getApiKey())) {
user.setAuthenticationType(AuthenticationType.WEB); updatedUser.setApiKey(customApiKey);
user.setApiKey(customApiKey); userRepository.save(updatedUser);
user.addAuthority(new Authority(Role.INTERNAL_API_USER.getRoleId(), user)); }
userRepository.save(user); },
() -> {
// Create new user with API role
User user = new User();
user.setUsername(username);
user.setPassword(UUID.randomUUID().toString());
user.setEnabled(true);
user.setFirstLogin(false);
user.setAuthenticationType(AuthenticationType.WEB);
user.setApiKey(customApiKey);
user.addAuthority(new Authority(Role.INTERNAL_API_USER.getRoleId(), user));
userRepository.save(user);
});
try {
databaseService.exportDatabase(); databaseService.exportDatabase();
} else { } catch (SQLException | UnsupportedProviderException e) {
// Update API key if it has changed log.error("Error exporting database after synchronising custom API user", e);
User user = existingUser.get();
if (!customApiKey.equals(user.getApiKey())) {
user.setApiKey(customApiKey);
userRepository.save(user);
databaseService.exportDatabase();
}
} }
} }

View File

@@ -14,7 +14,7 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig; import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.provider.UnsupportedProviderException; import stirling.software.SPDF.model.exception.UnsupportedProviderException;
@Slf4j @Slf4j
@Getter @Getter

View File

@@ -8,7 +8,7 @@ import org.springframework.stereotype.Component;
import stirling.software.SPDF.config.interfaces.DatabaseInterface; import stirling.software.SPDF.config.interfaces.DatabaseInterface;
import stirling.software.SPDF.controller.api.H2SQLCondition; import stirling.software.SPDF.controller.api.H2SQLCondition;
import stirling.software.SPDF.model.provider.UnsupportedProviderException; import stirling.software.SPDF.model.exception.UnsupportedProviderException;
@Component @Component
@Conditional(H2SQLCondition.class) @Conditional(H2SQLCondition.class)

View File

@@ -29,7 +29,7 @@ public class CustomOAuth2AuthenticationFailureHandler
if (exception instanceof BadCredentialsException) { if (exception instanceof BadCredentialsException) {
log.error("BadCredentialsException", exception); log.error("BadCredentialsException", exception);
getRedirectStrategy().sendRedirect(request, response, "/login?error=badcredentials"); getRedirectStrategy().sendRedirect(request, response, "/login?error=badCredentials");
return; return;
} }
if (exception instanceof DisabledException) { if (exception instanceof DisabledException) {
@@ -50,10 +50,12 @@ public class CustomOAuth2AuthenticationFailureHandler
if (error.getErrorCode().equals("Password must not be null")) { if (error.getErrorCode().equals("Password must not be null")) {
errorCode = "userAlreadyExistsWeb"; errorCode = "userAlreadyExistsWeb";
} }
log.error("OAuth2 Authentication error: " + errorCode);
log.error("OAuth2AuthenticationException", exception); log.error(
getRedirectStrategy().sendRedirect(request, response, "/login?erroroauth=" + errorCode); "OAuth2 Authentication error: {}",
return; errorCode != null ? errorCode : exception.getMessage(),
exception);
getRedirectStrategy().sendRedirect(request, response, "/login?errorOAuth=" + errorCode);
} }
log.error("Unhandled authentication exception", exception); log.error("Unhandled authentication exception", exception);
super.onAuthenticationFailure(request, response, exception); super.onAuthenticationFailure(request, response, exception);

View File

@@ -20,19 +20,18 @@ import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2; import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.AuthenticationType; import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.model.provider.UnsupportedProviderException; import stirling.software.SPDF.model.exception.UnsupportedProviderException;
import stirling.software.SPDF.utils.RequestUriUtils; import stirling.software.SPDF.utils.RequestUriUtils;
public class CustomOAuth2AuthenticationSuccessHandler public class CustomOAuth2AuthenticationSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler { extends SavedRequestAwareAuthenticationSuccessHandler {
private LoginAttemptService loginAttemptService; private final LoginAttemptService loginAttemptService;
private final ApplicationProperties applicationProperties;
private ApplicationProperties applicationProperties; private final UserService userService;
private UserService userService;
public CustomOAuth2AuthenticationSuccessHandler( public CustomOAuth2AuthenticationSuccessHandler(
final LoginAttemptService loginAttemptService, LoginAttemptService loginAttemptService,
ApplicationProperties applicationProperties, ApplicationProperties applicationProperties,
UserService userService) { UserService userService) {
this.applicationProperties = applicationProperties; this.applicationProperties = applicationProperties;
@@ -48,11 +47,9 @@ public class CustomOAuth2AuthenticationSuccessHandler
Object principal = authentication.getPrincipal(); Object principal = authentication.getPrincipal();
String username = ""; String username = "";
if (principal instanceof OAuth2User) { if (principal instanceof OAuth2User oauthUser) {
OAuth2User oauthUser = (OAuth2User) principal;
username = oauthUser.getName(); username = oauthUser.getName();
} else if (principal instanceof UserDetails) { } else if (principal instanceof UserDetails oauthUser) {
UserDetails oauthUser = (UserDetails) principal;
username = oauthUser.getUsername(); username = oauthUser.getUsername();
} }
@@ -78,6 +75,7 @@ public class CustomOAuth2AuthenticationSuccessHandler
throw new LockedException( throw new LockedException(
"Your account has been locked due to too many failed login attempts."); "Your account has been locked due to too many failed login attempts.");
} }
if (userService.isUserDisabled(username)) { if (userService.isUserDisabled(username)) {
getRedirectStrategy() getRedirectStrategy()
.sendRedirect(request, response, "/logout?userIsDisabled=true"); .sendRedirect(request, response, "/logout?userIsDisabled=true");
@@ -87,13 +85,14 @@ public class CustomOAuth2AuthenticationSuccessHandler
&& userService.hasPassword(username) && userService.hasPassword(username)
&& !userService.isAuthenticationTypeByUsername(username, AuthenticationType.SSO) && !userService.isAuthenticationTypeByUsername(username, AuthenticationType.SSO)
&& oAuth.getAutoCreateUser()) { && oAuth.getAutoCreateUser()) {
response.sendRedirect(contextPath + "/logout?oauth2AuthenticationErrorWeb=true"); response.sendRedirect(contextPath + "/logout?oAuth2AuthenticationErrorWeb=true");
return; return;
} }
try { try {
if (oAuth.getBlockRegistration() if (oAuth.getBlockRegistration()
&& !userService.usernameExistsIgnoreCase(username)) { && !userService.usernameExistsIgnoreCase(username)) {
response.sendRedirect(contextPath + "/logout?oauth2_admin_blocked_user=true"); response.sendRedirect(contextPath + "/logout?oAuth2AdminBlockedUser=true");
return; return;
} }
if (principal instanceof OAuth2User) { if (principal instanceof OAuth2User) {

View File

@@ -17,19 +17,19 @@ import stirling.software.SPDF.config.security.LoginAttemptService;
import stirling.software.SPDF.config.security.UserService; import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2; 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.User;
import stirling.software.SPDF.model.UsernameAttribute;
@Slf4j @Slf4j
public class CustomOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> { public class CustomOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
private final OidcUserService delegate = new OidcUserService(); private final OidcUserService delegate = new OidcUserService();
private UserService userService; private final UserService userService;
private LoginAttemptService loginAttemptService; private final LoginAttemptService loginAttemptService;
private ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
public CustomOAuth2UserService( public CustomOAuth2UserService(
ApplicationProperties applicationProperties, ApplicationProperties applicationProperties,
@@ -42,34 +42,26 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
@Override @Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2();
String usernameAttribute = oauth2.getUseAsUsername();
if (usernameAttribute == null || usernameAttribute.trim().isEmpty()) {
Client client = oauth2.getClient();
if (client != null && client.getKeycloak() != null) {
usernameAttribute = client.getKeycloak().getUseAsUsername();
} else {
usernameAttribute = "email";
}
}
try { try {
OidcUser user = delegate.loadUser(userRequest); OidcUser user = delegate.loadUser(userRequest);
String username = user.getUserInfo().getClaimAsString(usernameAttribute); OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2();
UsernameAttribute usernameAttribute =
UsernameAttribute.valueOf(oauth2.getUseAsUsername().toUpperCase());
String usernameAttributeKey = usernameAttribute.getName();
// Check if the username claim is null or empty // todo: save user by OIDC ID instead of username
if (username == null || username.trim().isEmpty()) { Optional<User> internalUser =
throw new IllegalArgumentException( userService.findByUsernameIgnoreCase(user.getAttribute(usernameAttributeKey));
"Claim '" + usernameAttribute + "' cannot be null or empty");
}
Optional<User> duser = userService.findByUsernameIgnoreCase(username); if (internalUser.isPresent()) {
if (duser.isPresent()) { String internalUsername = internalUser.get().getUsername();
if (loginAttemptService.isBlocked(username)) { if (loginAttemptService.isBlocked(internalUsername)) {
throw new LockedException( throw new LockedException(
"Your account has been locked due to too many failed login attempts."); "The account "
+ internalUsername
+ " has been locked due to too many failed login attempts.");
} }
if (userService.hasPassword(username)) { if (userService.hasPassword(usernameAttributeKey)) {
throw new IllegalArgumentException("Password must not be null"); throw new IllegalArgumentException("Password must not be null");
} }
} }
@@ -79,7 +71,7 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
user.getAuthorities(), user.getAuthorities(),
userRequest.getIdToken(), userRequest.getIdToken(),
user.getUserInfo(), user.getUserInfo(),
usernameAttribute); usernameAttributeKey);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
log.error("Error loading OIDC user: {}", e.getMessage()); log.error("Error loading OIDC user: {}", e.getMessage());
throw new OAuth2AuthenticationException(new OAuth2Error(e.getMessage()), e); throw new OAuth2AuthenticationException(new OAuth2Error(e.getMessage()), e);

View File

@@ -1,5 +1,8 @@
package stirling.software.SPDF.config.security.oauth2; package stirling.software.SPDF.config.security.oauth2;
import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE;
import static stirling.software.SPDF.utils.validation.Validator.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -26,18 +29,20 @@ import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2; import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client; import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
import stirling.software.SPDF.model.User; import stirling.software.SPDF.model.User;
import stirling.software.SPDF.model.provider.GithubProvider; import stirling.software.SPDF.model.UsernameAttribute;
import stirling.software.SPDF.model.exception.NoProviderFoundException;
import stirling.software.SPDF.model.provider.GitHubProvider;
import stirling.software.SPDF.model.provider.GoogleProvider; import stirling.software.SPDF.model.provider.GoogleProvider;
import stirling.software.SPDF.model.provider.KeycloakProvider; import stirling.software.SPDF.model.provider.KeycloakProvider;
import stirling.software.SPDF.model.provider.Provider;
@Configuration
@Slf4j @Slf4j
@ConditionalOnProperty( @Configuration
value = "security.oauth2.enabled", @ConditionalOnProperty(value = "security.oauth2.enabled", havingValue = "true")
havingValue = "true",
matchIfMissing = false)
public class OAuth2Configuration { public class OAuth2Configuration {
public static final String REDIRECT_URI_PATH = "{baseUrl}/login/oauth2/code/";
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
@Lazy private final UserService userService; @Lazy private final UserService userService;
@@ -48,139 +53,175 @@ public class OAuth2Configuration {
} }
@Bean @Bean
@ConditionalOnProperty( @ConditionalOnProperty(value = "security.oauth2.enabled", havingValue = "true")
value = "security.oauth2.enabled", public ClientRegistrationRepository clientRegistrationRepository()
havingValue = "true", throws NoProviderFoundException {
matchIfMissing = false)
public ClientRegistrationRepository clientRegistrationRepository() {
List<ClientRegistration> registrations = new ArrayList<>(); List<ClientRegistration> registrations = new ArrayList<>();
githubClientRegistration().ifPresent(registrations::add); githubClientRegistration().ifPresent(registrations::add);
oidcClientRegistration().ifPresent(registrations::add); oidcClientRegistration().ifPresent(registrations::add);
googleClientRegistration().ifPresent(registrations::add); googleClientRegistration().ifPresent(registrations::add);
keycloakClientRegistration().ifPresent(registrations::add); keycloakClientRegistration().ifPresent(registrations::add);
if (registrations.isEmpty()) { if (registrations.isEmpty()) {
log.error("At least one OAuth2 provider must be configured"); log.error("No OAuth2 provider registered");
System.exit(1); throw new NoProviderFoundException("At least one OAuth2 provider must be configured.");
} }
return new InMemoryClientRegistrationRepository(registrations); 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() { private Optional<ClientRegistration> keycloakClientRegistration() {
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2(); OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2();
if (oauth == null || !oauth.getEnabled()) {
if (isOAuth2Enabled(oauth2) || isClientInitialised(oauth2)) {
return Optional.empty(); return Optional.empty();
} }
Client client = oauth.getClient();
if (client == null) { Client client = oauth2.getClient();
return Optional.empty(); KeycloakProvider keycloakClient = client.getKeycloak();
} Provider keycloak =
KeycloakProvider keycloak = client.getKeycloak(); new KeycloakProvider(
return keycloak != null && keycloak.isSettingsValid() keycloakClient.getIssuer(),
keycloakClient.getClientId(),
keycloakClient.getClientSecret(),
keycloakClient.getScopes(),
keycloakClient.getUseAsUsername());
return validateProvider(keycloak)
? Optional.of( ? Optional.of(
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer()) ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
.registrationId(keycloak.getName()) .registrationId(keycloak.getName())
.clientId(keycloak.getClientId()) .clientId(keycloak.getClientId())
.clientSecret(keycloak.getClientSecret()) .clientSecret(keycloak.getClientSecret())
.scope(keycloak.getScopes()) .scope(keycloak.getScopes())
.userNameAttributeName(keycloak.getUseAsUsername()) .userNameAttributeName(keycloak.getUseAsUsername().getName())
.clientName(keycloak.getClientName()) .clientName(keycloak.getClientName())
.build()) .build())
: Optional.empty(); : Optional.empty();
} }
private Optional<ClientRegistration> googleClientRegistration() {
OAUTH2 oAuth2 = applicationProperties.getSecurity().getOauth2();
if (isOAuth2Enabled(oAuth2) || isClientInitialised(oAuth2)) {
return Optional.empty();
}
Client client = oAuth2.getClient();
GoogleProvider googleClient = client.getGoogle();
Provider google =
new GoogleProvider(
googleClient.getClientId(),
googleClient.getClientSecret(),
googleClient.getScopes(),
googleClient.getUseAsUsername());
return validateProvider(google)
? 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().getName())
.clientName(google.getClientName())
.redirectUri(REDIRECT_URI_PATH + google.getName())
.authorizationGrantType(AUTHORIZATION_CODE)
.build())
: Optional.empty();
}
private Optional<ClientRegistration> githubClientRegistration() { private Optional<ClientRegistration> githubClientRegistration() {
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2(); OAUTH2 oAuth2 = applicationProperties.getSecurity().getOauth2();
if (oauth == null || !oauth.getEnabled()) {
if (isOAuth2Enabled(oAuth2)) {
return Optional.empty(); return Optional.empty();
} }
Client client = oauth.getClient();
if (client == null) { Client client = oAuth2.getClient();
return Optional.empty(); GitHubProvider githubClient = client.getGithub();
} Provider github =
GithubProvider github = client.getGithub(); new GitHubProvider(
return github != null && github.isSettingsValid() githubClient.getClientId(),
githubClient.getClientSecret(),
githubClient.getScopes(),
githubClient.getUseAsUsername());
return validateProvider(github)
? Optional.of( ? Optional.of(
ClientRegistration.withRegistrationId(github.getName()) ClientRegistration.withRegistrationId(github.getName())
.clientId(github.getClientId()) .clientId(github.getClientId())
.clientSecret(github.getClientSecret()) .clientSecret(github.getClientSecret())
.scope(github.getScopes()) .scope(github.getScopes())
.authorizationUri(github.getAuthorizationuri()) .authorizationUri(github.getAuthorizationUri())
.tokenUri(github.getTokenuri()) .tokenUri(github.getTokenUri())
.userInfoUri(github.getUserinfouri()) .userInfoUri(github.getUserInfoUri())
.userNameAttributeName(github.getUseAsUsername()) .userNameAttributeName(github.getUseAsUsername().getName())
.clientName(github.getClientName()) .clientName(github.getClientName())
.redirectUri("{baseUrl}/login/oauth2/code/" + github.getName()) .redirectUri(REDIRECT_URI_PATH + github.getName())
.authorizationGrantType( .authorizationGrantType(AUTHORIZATION_CODE)
org.springframework.security.oauth2.core
.AuthorizationGrantType.AUTHORIZATION_CODE)
.build()) .build())
: Optional.empty(); : Optional.empty();
} }
private Optional<ClientRegistration> oidcClientRegistration() { private Optional<ClientRegistration> oidcClientRegistration() {
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2(); OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
if (oauth == null
|| oauth.getIssuer() == null if (isOAuth2Enabled(oauth) || isClientInitialised(oauth)) {
|| 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.empty();
} }
return Optional.of(
ClientRegistrations.fromIssuerLocation(oauth.getIssuer()) String name = oauth.getProvider();
.registrationId("oidc") String firstChar = String.valueOf(name.charAt(0));
.clientId(oauth.getClientId()) String clientName = name.replaceFirst(firstChar, firstChar.toUpperCase());
.clientSecret(oauth.getClientSecret())
.scope(oauth.getScopes()) Provider oidcProvider =
.userNameAttributeName(oauth.getUseAsUsername()) new Provider(
.clientName("OIDC") oauth.getIssuer(),
.build()); name,
clientName,
oauth.getClientId(),
oauth.getClientSecret(),
oauth.getScopes(),
UsernameAttribute.valueOf(oauth.getUseAsUsername().toUpperCase()),
oauth.getLogoutUrl(),
null,
null,
null);
return !isStringEmpty(oidcProvider.getIssuer()) || validateProvider(oidcProvider)
? Optional.of(
ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
.registrationId(name)
.clientId(oidcProvider.getClientId())
.clientSecret(oidcProvider.getClientSecret())
.scope(oidcProvider.getScopes())
.userNameAttributeName(oidcProvider.getUseAsUsername().getName())
.clientName(clientName)
.redirectUri(REDIRECT_URI_PATH + name)
.authorizationGrantType(AUTHORIZATION_CODE)
.build())
: Optional.empty();
}
private boolean isOAuth2Enabled(OAUTH2 oAuth2) {
return oAuth2 == null || !oAuth2.getEnabled();
}
private boolean isClientInitialised(OAUTH2 oauth2) {
Client client = oauth2.getClient();
return client == null;
} }
/* /*
This following function is to grant Authorities to the OAUTH2 user from the values stored in the database. 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. This is required for the internal; 'hasRole()' function to give out the correct role.
*/ */
@Bean @Bean
@ConditionalOnProperty( @ConditionalOnProperty(value = "security.oauth2.enabled", havingValue = "true")
value = "security.oauth2.enabled",
havingValue = "true",
matchIfMissing = false)
GrantedAuthoritiesMapper userAuthoritiesMapper() { GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities) -> { return (authorities) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>(); Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
@@ -200,11 +241,9 @@ public class OAuth2Configuration {
(String) oauth2Auth.getAttributes().get(useAsUsername)); (String) oauth2Auth.getAttributes().get(useAsUsername));
if (userOpt.isPresent()) { if (userOpt.isPresent()) {
User user = userOpt.get(); User user = userOpt.get();
if (user != null) { mappedAuthorities.add(
mappedAuthorities.add( new SimpleGrantedAuthority(
new SimpleGrantedAuthority( userService.findRole(user).getAuthority()));
userService.findRole(user).getAuthority()));
}
} }
} }
}); });

View File

@@ -13,8 +13,10 @@ import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader; import org.bouncycastle.util.io.pem.PemReader;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
public class CertificateUtils { public class CertificateUtils {
public static X509Certificate readCertificate(Resource certificateResource) throws Exception { public static X509Certificate readCertificate(Resource certificateResource) throws Exception {

View File

@@ -4,27 +4,13 @@ import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
public class CustomSaml2AuthenticatedPrincipal @ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
public record CustomSaml2AuthenticatedPrincipal(String name, Map<String, List<Object>> attributes, String nameId, List<String> sessionIndexes)
implements Saml2AuthenticatedPrincipal, Serializable { implements Saml2AuthenticatedPrincipal, Serializable {
private final String name;
private final Map<String, List<Object>> attributes;
private final String nameId;
private final List<String> sessionIndexes;
public CustomSaml2AuthenticatedPrincipal(
String name,
Map<String, List<Object>> attributes,
String nameId,
List<String> sessionIndexes) {
this.name = name;
this.attributes = attributes;
this.nameId = nameId;
this.sessionIndexes = sessionIndexes;
}
@Override @Override
public String getName() { public String getName() {
return this.name; return this.name;
@@ -35,11 +21,4 @@ public class CustomSaml2AuthenticatedPrincipal
return this.attributes; return this.attributes;
} }
public String getNameId() {
return this.nameId;
}
public List<String> getSessionIndexes() {
return this.sessionIndexes;
}
} }

View File

@@ -2,19 +2,20 @@ package stirling.software.SPDF.config.security.saml2;
import java.io.IOException; import java.io.IOException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.authentication.ProviderNotFoundException; import org.springframework.security.authentication.ProviderNotFoundException;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.saml2.core.Saml2Error; import org.springframework.security.saml2.core.Saml2Error;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override @Override
@@ -22,18 +23,19 @@ public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthentica
HttpServletRequest request, HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response,
AuthenticationException exception) AuthenticationException exception)
throws IOException, ServletException { throws IOException {
log.error("Authentication error", exception);
if (exception instanceof Saml2AuthenticationException) { if (exception instanceof Saml2AuthenticationException) {
Saml2Error error = ((Saml2AuthenticationException) exception).getSaml2Error(); Saml2Error error = ((Saml2AuthenticationException) exception).getSaml2Error();
getRedirectStrategy() getRedirectStrategy()
.sendRedirect(request, response, "/login?erroroauth=" + error.getErrorCode()); .sendRedirect(request, response, "/login?errorOAuth=" + error.getErrorCode());
} else if (exception instanceof ProviderNotFoundException) { } else if (exception instanceof ProviderNotFoundException) {
getRedirectStrategy() getRedirectStrategy()
.sendRedirect( .sendRedirect(
request, request,
response, response,
"/login?erroroauth=not_authentication_provider_found"); "/login?errorOAuth=not_authentication_provider_found");
} }
log.error("AuthenticationException: " + exception);
} }
} }

View File

@@ -21,7 +21,7 @@ import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2; import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
import stirling.software.SPDF.model.AuthenticationType; import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.model.provider.UnsupportedProviderException; import stirling.software.SPDF.model.exception.UnsupportedProviderException;
import stirling.software.SPDF.utils.RequestUriUtils; import stirling.software.SPDF.utils.RequestUriUtils;
@AllArgsConstructor @AllArgsConstructor
@@ -42,7 +42,7 @@ public class CustomSaml2AuthenticationSuccessHandler
log.debug("Starting SAML2 authentication success handling"); log.debug("Starting SAML2 authentication success handling");
if (principal instanceof CustomSaml2AuthenticatedPrincipal) { if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
String username = ((CustomSaml2AuthenticatedPrincipal) principal).getName(); String username = ((CustomSaml2AuthenticatedPrincipal) principal).name();
log.debug("Authenticated principal found for user: {}", username); log.debug("Authenticated principal found for user: {}", username);
HttpSession session = request.getSession(false); HttpSession session = request.getSession(false);
@@ -97,7 +97,7 @@ public class CustomSaml2AuthenticationSuccessHandler
"User {} exists with password but is not SSO user, redirecting to logout", "User {} exists with password but is not SSO user, redirecting to logout",
username); username);
response.sendRedirect( response.sendRedirect(
contextPath + "/logout?oauth2AuthenticationErrorWeb=true"); contextPath + "/logout?oAuth2AuthenticationErrorWeb=true");
return; return;
} }
@@ -105,20 +105,18 @@ public class CustomSaml2AuthenticationSuccessHandler
if (saml2.getBlockRegistration() && !userExists) { if (saml2.getBlockRegistration() && !userExists) {
log.debug("Registration blocked for new user: {}", username); log.debug("Registration blocked for new user: {}", username);
response.sendRedirect( response.sendRedirect(
contextPath + "/login?erroroauth=oauth2_admin_blocked_user"); contextPath + "/login?errorOAuth=oAuth2AdminBlockedUser");
return; return;
} }
log.debug("Processing SSO post-login for user: {}", username); log.debug("Processing SSO post-login for user: {}", username);
userService.processSSOPostLogin(username, saml2.getAutoCreateUser()); userService.processSSOPostLogin(username, saml2.getAutoCreateUser());
log.debug("Successfully processed authentication for user: {}", username); log.debug("Successfully processed authentication for user: {}", username);
response.sendRedirect(contextPath + "/"); response.sendRedirect(contextPath + "/");
return;
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) { } catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
log.debug( log.debug(
"Invalid username detected for user: {}, redirecting to logout", "Invalid username detected for user: {}, redirecting to logout",
username); username);
response.sendRedirect(contextPath + "/logout?invalidUsername=true"); response.sendRedirect(contextPath + "/logout?invalidUsername=true");
return;
} }
} }
} else { } else {

View File

@@ -7,6 +7,7 @@ import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.Attribute; import org.opensaml.saml.saml2.core.Attribute;
import org.opensaml.saml.saml2.core.AttributeStatement; import org.opensaml.saml.saml2.core.AttributeStatement;
import org.opensaml.saml.saml2.core.AuthnStatement; import org.opensaml.saml.saml2.core.AuthnStatement;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.security.core.authority.SimpleGrantedAuthority; 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.OpenSaml4AuthenticationProvider.ResponseToken;
@@ -18,10 +19,11 @@ import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.User; import stirling.software.SPDF.model.User;
@Slf4j @Slf4j
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
public class CustomSaml2ResponseAuthenticationConverter public class CustomSaml2ResponseAuthenticationConverter
implements Converter<ResponseToken, Saml2Authentication> { implements Converter<ResponseToken, Saml2Authentication> {
private UserService userService; private final UserService userService;
public CustomSaml2ResponseAuthenticationConverter(UserService userService) { public CustomSaml2ResponseAuthenticationConverter(UserService userService) {
this.userService = userService; this.userService = userService;
@@ -61,10 +63,10 @@ public class CustomSaml2ResponseAuthenticationConverter
Map<String, List<Object>> attributes = extractAttributes(assertion); Map<String, List<Object>> attributes = extractAttributes(assertion);
// Debug log with actual values // Debug log with actual values
log.debug("Extracted SAML Attributes: " + attributes); log.debug("Extracted SAML Attributes: {}", attributes);
// Try to get username/identifier in order of preference // Try to get username/identifier in order of preference
String userIdentifier = null; String userIdentifier;
if (hasAttribute(attributes, "username")) { if (hasAttribute(attributes, "username")) {
userIdentifier = getFirstAttributeValue(attributes, "username"); userIdentifier = getFirstAttributeValue(attributes, "username");
} else if (hasAttribute(attributes, "emailaddress")) { } else if (hasAttribute(attributes, "emailaddress")) {
@@ -84,10 +86,8 @@ public class CustomSaml2ResponseAuthenticationConverter
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_USER"); SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
if (userOpt.isPresent()) { if (userOpt.isPresent()) {
User user = userOpt.get(); User user = userOpt.get();
if (user != null) { simpleGrantedAuthority =
simpleGrantedAuthority = new SimpleGrantedAuthority(userService.findRole(user).getAuthority());
new SimpleGrantedAuthority(userService.findRole(user).getAuthority());
}
} }
List<String> sessionIndexes = new ArrayList<>(); List<String> sessionIndexes = new ArrayList<>();
@@ -102,7 +102,7 @@ public class CustomSaml2ResponseAuthenticationConverter
return new Saml2Authentication( return new Saml2Authentication(
principal, principal,
responseToken.getToken().getSaml2Response(), responseToken.getToken().getSaml2Response(),
Collections.singletonList(simpleGrantedAuthority)); List.of(simpleGrantedAuthority));
} }
private boolean hasAttribute(Map<String, List<Object>> attributes, String name) { private boolean hasAttribute(Map<String, List<Object>> attributes, String name) {

View File

@@ -11,10 +11,12 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType; import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; 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.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; 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.registration.Saml2MessageBinding;
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver; import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@@ -26,27 +28,20 @@ import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
@Configuration @Configuration
@Slf4j @Slf4j
@ConditionalOnProperty( @ConditionalOnProperty(value = "security.saml2.enabled", havingValue = "true")
value = "security.saml2.enabled",
havingValue = "true",
matchIfMissing = false)
public class SAML2Configuration { public class SAML2Configuration {
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
public SAML2Configuration(ApplicationProperties applicationProperties) { public SAML2Configuration(ApplicationProperties applicationProperties) {
this.applicationProperties = applicationProperties; this.applicationProperties = applicationProperties;
} }
@Bean @Bean
@ConditionalOnProperty( @ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
name = "security.saml2.enabled",
havingValue = "true",
matchIfMissing = false)
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception { public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
SAML2 samlConf = applicationProperties.getSecurity().getSaml2(); SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert()); X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getIdpCert());
Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert); Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
Resource privateKeyResource = samlConf.getPrivateKey(); Resource privateKeyResource = samlConf.getPrivateKey();
Resource certificateResource = samlConf.getSpCert(); Resource certificateResource = samlConf.getSpCert();
@@ -58,81 +53,124 @@ public class SAML2Configuration {
RelyingPartyRegistration rp = RelyingPartyRegistration rp =
RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId()) RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
.signingX509Credentials(c -> c.add(signingCredential)) .signingX509Credentials(c -> c.add(signingCredential))
.entityId(samlConf.getIdpIssuer())
.singleLogoutServiceBinding(Saml2MessageBinding.POST)
.singleLogoutServiceLocation(samlConf.getIdpSingleLogoutUrl())
.singleLogoutServiceResponseLocation("http://localhost:8080/login")
.assertionConsumerServiceBinding(Saml2MessageBinding.POST)
.assertionConsumerServiceLocation(
"{baseUrl}/login/saml2/sso/{registrationId}")
.assertingPartyMetadata( .assertingPartyMetadata(
metadata -> metadata ->
metadata.entityId(samlConf.getIdpIssuer()) metadata.entityId(samlConf.getIdpIssuer())
.singleSignOnServiceLocation(
samlConf.getIdpSingleLoginUrl())
.verificationX509Credentials( .verificationX509Credentials(
c -> c.add(verificationCredential)) c -> c.add(verificationCredential))
.singleSignOnServiceBinding( .singleSignOnServiceBinding(
Saml2MessageBinding.POST) Saml2MessageBinding.POST)
.singleSignOnServiceLocation(
samlConf.getIdpSingleLoginUrl())
.singleLogoutServiceBinding(
Saml2MessageBinding.POST)
.singleLogoutServiceLocation(
samlConf.getIdpSingleLogoutUrl())
.wantAuthnRequestsSigned(true)) .wantAuthnRequestsSigned(true))
.build(); .build();
return new InMemoryRelyingPartyRegistrationRepository(rp); return new InMemoryRelyingPartyRegistrationRepository(rp);
} }
@Bean @Bean
@ConditionalOnProperty( @ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
name = "security.saml2.enabled",
havingValue = "true",
matchIfMissing = false)
public OpenSaml4AuthenticationRequestResolver authenticationRequestResolver( public OpenSaml4AuthenticationRequestResolver authenticationRequestResolver(
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) { RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
OpenSaml4AuthenticationRequestResolver resolver = OpenSaml4AuthenticationRequestResolver resolver =
new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationRepository); new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationRepository);
resolver.setAuthnRequestCustomizer( resolver.setAuthnRequestCustomizer(
customizer -> { 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(); HttpServletRequest request = customizer.getRequest();
// Log HTTP request details AuthnRequest authnRequest = customizer.getAuthnRequest();
log.debug("HTTP Request Method: {}", request.getMethod()); HttpSessionSaml2AuthenticationRequestRepository requestRepository =
log.debug("Request URI: {}", request.getRequestURI()); new HttpSessionSaml2AuthenticationRequestRepository();
log.debug("Request URL: {}", request.getRequestURL().toString()); AbstractSaml2AuthenticationRequest saml2AuthenticationRequest =
log.debug("Query String: {}", request.getQueryString()); requestRepository.loadAuthenticationRequest(request);
log.debug("Remote Address: {}", request.getRemoteAddr());
// Log headers if (saml2AuthenticationRequest != null) {
Collections.list(request.getHeaderNames()) String sessionId = request.getSession(false).getId();
.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( log.debug(
"AssertionConsumerServiceURL: {}", "Retrieving SAML 2 authentication request ID from the current HTTP session {}",
authnRequest.getAssertionConsumerServiceURL()); sessionId);
}
// Log NameID policy if present String authenticationRequestId = saml2AuthenticationRequest.getId();
if (authnRequest.getNameIDPolicy() != null) {
log.debug( if (!authenticationRequestId.isBlank()) {
"NameIDPolicy Format: {}", authnRequest.setID(authenticationRequestId);
authnRequest.getNameIDPolicy().getFormat()); } else {
log.warn(
"No authentication request found for HTTP session {}. Generating new ID",
sessionId);
authnRequest.setID("ARQ" + UUID.randomUUID().toString().substring(1));
}
} else {
log.debug("Generating new authentication request ID");
authnRequest.setID("ARQ" + UUID.randomUUID().toString().substring(1));
} }
logAuthnRequestDetails(authnRequest);
logHttpRequestDetails(request);
}); });
return resolver; return resolver;
} }
private static void logAuthnRequestDetails(AuthnRequest authnRequest) {
String message =
"""
AuthnRequest:
ID: {}
Issuer: {}
IssueInstant: {}
AssertionConsumerService (ACS) URL: {}
""";
log.debug(
message,
authnRequest.getID(),
authnRequest.getIssuer() != null ? authnRequest.getIssuer().getValue() : null,
authnRequest.getIssueInstant(),
authnRequest.getAssertionConsumerServiceURL());
if (authnRequest.getNameIDPolicy() != null) {
log.debug("NameIDPolicy Format: {}", authnRequest.getNameIDPolicy().getFormat());
}
}
private static void logHttpRequestDetails(HttpServletRequest request) {
log.debug("HTTP Headers: ");
Collections.list(request.getHeaderNames())
.forEach(
headerName ->
log.debug("{}: {}", headerName, request.getHeader(headerName)));
String message =
"""
HTTP Request Method: {}
Session ID: {}
Request Path: {}
Query String: {}
Remote Address: {}
SAML Request Parameters:
SAMLRequest: {}
RelayState: {}
""";
log.debug(
message,
request.getMethod(),
request.getSession().getId(),
request.getRequestURI(),
request.getQueryString(),
request.getRemoteAddr(),
request.getParameter("SAMLRequest"),
request.getParameter("RelayState"));
}
} }

View File

@@ -48,7 +48,7 @@ public class SessionPersistentRegistry implements SessionRegistry {
} else if (principal instanceof OAuth2User) { } else if (principal instanceof OAuth2User) {
principalName = ((OAuth2User) principal).getName(); principalName = ((OAuth2User) principal).getName();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) { } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
principalName = ((CustomSaml2AuthenticatedPrincipal) principal).getName(); principalName = ((CustomSaml2AuthenticatedPrincipal) principal).name();
} else if (principal instanceof String) { } else if (principal instanceof String) {
principalName = (String) principal; principalName = (String) principal;
} }
@@ -79,7 +79,7 @@ public class SessionPersistentRegistry implements SessionRegistry {
} else if (principal instanceof OAuth2User) { } else if (principal instanceof OAuth2User) {
principalName = ((OAuth2User) principal).getName(); principalName = ((OAuth2User) principal).getName();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) { } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
principalName = ((CustomSaml2AuthenticatedPrincipal) principal).getName(); principalName = ((CustomSaml2AuthenticatedPrincipal) principal).name();
} else if (principal instanceof String) { } else if (principal instanceof String) {
principalName = (String) principal; principalName = (String) principal;
} }

View File

@@ -36,7 +36,7 @@ import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.Role;
import stirling.software.SPDF.model.User; import stirling.software.SPDF.model.User;
import stirling.software.SPDF.model.api.user.UsernameAndPass; import stirling.software.SPDF.model.api.user.UsernameAndPass;
import stirling.software.SPDF.model.provider.UnsupportedProviderException; import stirling.software.SPDF.model.exception.UnsupportedProviderException;
@Controller @Controller
@Tag(name = "User", description = "User APIs") @Tag(name = "User", description = "User APIs")
@@ -126,7 +126,7 @@ public class UserController {
return new RedirectView("/change-creds?messageType=notAuthenticated", true); return new RedirectView("/change-creds?messageType=notAuthenticated", true);
} }
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName()); Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
if (userOpt == null || userOpt.isEmpty()) { if (userOpt.isEmpty()) {
return new RedirectView("/change-creds?messageType=userNotFound", true); return new RedirectView("/change-creds?messageType=userNotFound", true);
} }
User user = userOpt.get(); User user = userOpt.get();
@@ -154,7 +154,7 @@ public class UserController {
return new RedirectView("/account?messageType=notAuthenticated", true); return new RedirectView("/account?messageType=notAuthenticated", true);
} }
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName()); Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
if (userOpt == null || userOpt.isEmpty()) { if (userOpt.isEmpty()) {
return new RedirectView("/account?messageType=userNotFound", true); return new RedirectView("/account?messageType=userNotFound", true);
} }
User user = userOpt.get(); User user = userOpt.get();
@@ -176,7 +176,7 @@ public class UserController {
for (Map.Entry<String, String[]> entry : paramMap.entrySet()) { for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
updates.put(entry.getKey(), entry.getValue()[0]); updates.put(entry.getKey(), entry.getValue()[0]);
} }
log.debug("Processed updates: " + updates); log.debug("Processed updates: {}", updates);
// Assuming you have a method in userService to update the settings for a user // Assuming you have a method in userService to update the settings for a user
userService.updateUserSettings(principal.getName(), updates); userService.updateUserSettings(principal.getName(), updates);
// Redirect to a page of your choice after updating // Redirect to a page of your choice after updating
@@ -199,7 +199,7 @@ public class UserController {
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username); Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
if (userOpt.isPresent()) { if (userOpt.isPresent()) {
User user = userOpt.get(); User user = userOpt.get();
if (user != null && user.getUsername().equalsIgnoreCase(username)) { if (user.getUsername().equalsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=usernameExists", true); return new RedirectView("/addUsers?messageType=usernameExists", true);
} }
} }
@@ -276,7 +276,7 @@ public class UserController {
Authentication authentication) Authentication authentication)
throws SQLException, UnsupportedProviderException { throws SQLException, UnsupportedProviderException {
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username); Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
if (!userOpt.isPresent()) { if (userOpt.isEmpty()) {
return new RedirectView("/addUsers?messageType=userNotFound", true); return new RedirectView("/addUsers?messageType=userNotFound", true);
} }
if (!userService.usernameExistsIgnoreCase(username)) { if (!userService.usernameExistsIgnoreCase(username)) {
@@ -295,20 +295,20 @@ public class UserController {
List<Object> principals = sessionRegistry.getAllPrincipals(); List<Object> principals = sessionRegistry.getAllPrincipals();
String userNameP = ""; String userNameP = "";
for (Object principal : principals) { for (Object principal : principals) {
List<SessionInformation> sessionsInformations = List<SessionInformation> sessionsInformation =
sessionRegistry.getAllSessions(principal, false); sessionRegistry.getAllSessions(principal, false);
if (principal instanceof UserDetails) { if (principal instanceof UserDetails) {
userNameP = ((UserDetails) principal).getUsername(); userNameP = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) { } else if (principal instanceof OAuth2User) {
userNameP = ((OAuth2User) principal).getName(); userNameP = ((OAuth2User) principal).getName();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) { } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
userNameP = ((CustomSaml2AuthenticatedPrincipal) principal).getName(); userNameP = ((CustomSaml2AuthenticatedPrincipal) principal).name();
} else if (principal instanceof String) { } else if (principal instanceof String) {
userNameP = (String) principal; userNameP = (String) principal;
} }
if (userNameP.equalsIgnoreCase(username)) { if (userNameP.equalsIgnoreCase(username)) {
for (SessionInformation sessionsInformation : sessionsInformations) { for (SessionInformation sessionInfo : sessionsInformation) {
sessionRegistry.expireSession(sessionsInformation.getSessionId()); sessionRegistry.expireSession(sessionInfo.getSessionId());
} }
} }
} }

View File

@@ -52,7 +52,8 @@ public class CompressController {
this.pdfDocumentFactory = pdfDocumentFactory; this.pdfDocumentFactory = pdfDocumentFactory;
} }
private void compressImagesInPDF(Path pdfFile, double initialScaleFactor) throws Exception { private void compressImagesInPDF(Path pdfFile, double initialScaleFactor, boolean grayScale)
throws Exception {
byte[] fileBytes = Files.readAllBytes(pdfFile); byte[] fileBytes = Files.readAllBytes(pdfFile);
try (PDDocument doc = Loader.loadPDF(fileBytes)) { try (PDDocument doc = Loader.loadPDF(fileBytes)) {
double scaleFactor = initialScaleFactor; double scaleFactor = initialScaleFactor;
@@ -77,11 +78,23 @@ public class CompressController {
bufferedImage.getScaledInstance( bufferedImage.getScaledInstance(
newWidth, newHeight, Image.SCALE_SMOOTH); newWidth, newHeight, Image.SCALE_SMOOTH);
BufferedImage scaledBufferedImage = BufferedImage scaledBufferedImage;
new BufferedImage( if (grayScale
newWidth, newHeight, BufferedImage.TYPE_INT_RGB); || bufferedImage.getType() == BufferedImage.TYPE_BYTE_GRAY) {
scaledBufferedImage.getGraphics().drawImage(scaledImage, 0, 0, null); scaledBufferedImage =
new BufferedImage(
newWidth, newHeight, BufferedImage.TYPE_BYTE_GRAY);
scaledBufferedImage
.getGraphics()
.drawImage(scaledImage, 0, 0, null);
} else {
scaledBufferedImage =
new BufferedImage(
newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
scaledBufferedImage
.getGraphics()
.drawImage(scaledImage, 0, 0, null);
}
ByteArrayOutputStream compressedImageStream = ByteArrayOutputStream compressedImageStream =
new ByteArrayOutputStream(); new ByteArrayOutputStream();
ImageIO.write(scaledBufferedImage, "jpeg", compressedImageStream); ImageIO.write(scaledBufferedImage, "jpeg", compressedImageStream);
@@ -140,6 +153,7 @@ public class CompressController {
} }
boolean sizeMet = false; boolean sizeMet = false;
boolean grayscaleEnabled = Boolean.TRUE.equals(request.getGrayscale());
while (!sizeMet && optimizeLevel <= 9) { while (!sizeMet && optimizeLevel <= 9) {
// Apply additional image compression for levels 6-9 // Apply additional image compression for levels 6-9
@@ -153,7 +167,7 @@ public class CompressController {
case 9 -> 0.5; // 60% of original size case 9 -> 0.5; // 60% of original size
default -> 1.0; default -> 1.0;
}; };
compressImagesInPDF(tempInputFile, scaleFactor); compressImagesInPDF(tempInputFile, scaleFactor, grayscaleEnabled);
} }
// Run QPDF optimization // Run QPDF optimization
@@ -170,6 +184,7 @@ public class CompressController {
command.add("--compression-level=" + optimizeLevel); command.add("--compression-level=" + optimizeLevel);
command.add("--compress-streams=y"); command.add("--compress-streams=y");
command.add("--object-streams=generate"); command.add("--object-streams=generate");
command.add("--no-warn");
command.add(tempInputFile.toString()); command.add(tempInputFile.toString());
command.add(tempOutputFile.toString()); command.add(tempOutputFile.toString());

View File

@@ -1,8 +1,15 @@
package stirling.software.SPDF.controller.web; package stirling.software.SPDF.controller.web;
import static stirling.software.SPDF.utils.validation.Validator.validateProvider;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.*; import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@@ -24,12 +31,16 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal; import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry; import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.*; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security; import stirling.software.SPDF.model.ApplicationProperties.Security;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2; import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client; import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2; import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
import stirling.software.SPDF.model.provider.GithubProvider; import stirling.software.SPDF.model.Authority;
import stirling.software.SPDF.model.Role;
import stirling.software.SPDF.model.SessionEntity;
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.GoogleProvider;
import stirling.software.SPDF.model.provider.KeycloakProvider; import stirling.software.SPDF.model.provider.KeycloakProvider;
import stirling.software.SPDF.repository.UserRepository; import stirling.software.SPDF.repository.UserRepository;
@@ -39,12 +50,12 @@ import stirling.software.SPDF.repository.UserRepository;
@Tag(name = "Account Security", description = "Account Security APIs") @Tag(name = "Account Security", description = "Account Security APIs")
public class AccountWebController { public class AccountWebController {
public static final String OAUTH_2_AUTHORIZATION = "/oauth2/authorization/";
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
private final SessionPersistentRegistry sessionPersistentRegistry; private final SessionPersistentRegistry sessionPersistentRegistry;
// Assuming you have a repository for user operations
private final UserRepository // Assuming you have a repository for user operations private final UserRepository userRepository;
userRepository;
public AccountWebController( public AccountWebController(
ApplicationProperties applicationProperties, ApplicationProperties applicationProperties,
@@ -61,132 +72,127 @@ public class AccountWebController {
if (authentication != null && authentication.isAuthenticated()) { if (authentication != null && authentication.isAuthenticated()) {
return "redirect:/"; return "redirect:/";
} }
Map<String, String> providerList = new HashMap<>(); Map<String, String> providerList = new HashMap<>();
Security securityProps = applicationProperties.getSecurity(); Security securityProps = applicationProperties.getSecurity();
OAUTH2 oauth = securityProps.getOauth2(); OAUTH2 oauth = securityProps.getOauth2();
if (oauth != null) { if (oauth != null) {
if (oauth.getEnabled()) { if (oauth.getEnabled()) {
if (oauth.isSettingsValid()) { if (oauth.isSettingsValid()) {
providerList.put("/oauth2/authorization/oidc", oauth.getProvider()); String firstChar = String.valueOf(oauth.getProvider().charAt(0));
String clientName =
oauth.getProvider().replaceFirst(firstChar, firstChar.toUpperCase());
providerList.put(OAUTH_2_AUTHORIZATION + oauth.getProvider(), clientName);
} }
Client client = oauth.getClient(); Client client = oauth.getClient();
if (client != null) { if (client != null) {
GoogleProvider google = client.getGoogle(); GoogleProvider google = client.getGoogle();
if (google.isSettingsValid()) {
if (validateProvider(google)) {
providerList.put( providerList.put(
"/oauth2/authorization/" + google.getName(), OAUTH_2_AUTHORIZATION + google.getName(), google.getClientName());
google.getClientName());
} }
GithubProvider github = client.getGithub();
if (github.isSettingsValid()) { GitHubProvider github = client.getGithub();
if (validateProvider(github)) {
providerList.put( providerList.put(
"/oauth2/authorization/" + github.getName(), OAUTH_2_AUTHORIZATION + github.getName(), github.getClientName());
github.getClientName());
} }
KeycloakProvider keycloak = client.getKeycloak(); KeycloakProvider keycloak = client.getKeycloak();
if (keycloak.isSettingsValid()) {
if (validateProvider(keycloak)) {
providerList.put( providerList.put(
"/oauth2/authorization/" + keycloak.getName(), OAUTH_2_AUTHORIZATION + keycloak.getName(),
keycloak.getClientName()); keycloak.getClientName());
} }
} }
} }
} }
SAML2 saml2 = securityProps.getSaml2(); SAML2 saml2 = securityProps.getSaml2();
if (securityProps.isSaml2Activ()
&& applicationProperties.getSystem().getEnableAlphaFunctionality()) { if (securityProps.isSaml2Active()
providerList.put("/saml2/authenticate/" + saml2.getRegistrationId(), "SAML 2"); && applicationProperties.getSystem().getEnableAlphaFunctionality()
&& applicationProperties.getEnterpriseEdition().isEnabled()) {
String samlIdp = saml2.getProvider();
String saml2AuthenticationPath = "/saml2/authenticate/" + saml2.getRegistrationId();
if (applicationProperties.getEnterpriseEdition().isSsoAutoLogin()) {
return "redirect:"
+ request.getRequestURL()
+ saml2AuthenticationPath;
} else {
providerList.put(saml2AuthenticationPath, samlIdp + " (SAML 2)");
}
} }
// Remove any null keys/values from the providerList // Remove any null keys/values from the providerList
providerList providerList
.entrySet() .entrySet()
.removeIf(entry -> entry.getKey() == null || entry.getValue() == null); .removeIf(entry -> entry.getKey() == null || entry.getValue() == null);
model.addAttribute("providerlist", providerList); model.addAttribute("providerList", providerList);
model.addAttribute("loginMethod", securityProps.getLoginMethod()); model.addAttribute("loginMethod", securityProps.getLoginMethod());
boolean altLogin = providerList.size() > 0 ? securityProps.isAltLogin() : false;
boolean altLogin = !providerList.isEmpty() ? securityProps.isAltLogin() : false;
model.addAttribute("altLogin", altLogin); model.addAttribute("altLogin", altLogin);
model.addAttribute("currentPage", "login"); model.addAttribute("currentPage", "login");
String error = request.getParameter("error"); String error = request.getParameter("error");
if (error != null) { if (error != null) {
switch (error) { switch (error) {
case "badcredentials": case "badCredentials" -> error = "login.invalid";
error = "login.invalid"; case "locked" -> error = "login.locked";
break; case "oauth2AuthenticationError" -> error = "userAlreadyExistsOAuthMessage";
case "locked":
error = "login.locked";
break;
case "oauth2AuthenticationError":
error = "userAlreadyExistsOAuthMessage";
break;
default:
break;
} }
model.addAttribute("error", error); model.addAttribute("error", error);
} }
String erroroauth = request.getParameter("erroroauth");
if (erroroauth != null) { String errorOAuth = request.getParameter("errorOAuth");
switch (erroroauth) {
case "oauth2AutoCreateDisabled": if (errorOAuth != null) {
erroroauth = "login.oauth2AutoCreateDisabled"; switch (errorOAuth) {
break; case "oAuth2AutoCreateDisabled" -> errorOAuth = "login.oAuth2AutoCreateDisabled";
case "invalidUsername": case "invalidUsername" -> errorOAuth = "login.invalid";
erroroauth = "login.invalid"; case "userAlreadyExistsWeb" -> errorOAuth = "userAlreadyExistsWebMessage";
break; case "oAuth2AuthenticationErrorWeb" -> errorOAuth = "login.oauth2InvalidUserType";
case "userAlreadyExistsWeb": case "invalid_token_response" -> errorOAuth = "login.oauth2InvalidTokenResponse";
erroroauth = "userAlreadyExistsWebMessage"; case "authorization_request_not_found" ->
break; errorOAuth = "login.oauth2RequestNotFound";
case "oauth2AuthenticationErrorWeb": case "access_denied" -> errorOAuth = "login.oauth2AccessDenied";
erroroauth = "login.oauth2InvalidUserType"; case "invalid_user_info_response" ->
break; errorOAuth = "login.oauth2InvalidUserInfoResponse";
case "invalid_token_response": case "invalid_request" -> errorOAuth = "login.oauth2invalidRequest";
erroroauth = "login.oauth2InvalidTokenResponse"; case "invalid_id_token" -> errorOAuth = "login.oauth2InvalidIdToken";
break; case "oAuth2AdminBlockedUser" -> errorOAuth = "login.oAuth2AdminBlockedUser";
case "authorization_request_not_found": case "userIsDisabled" -> errorOAuth = "login.userIsDisabled";
erroroauth = "login.oauth2RequestNotFound"; case "invalid_destination" -> errorOAuth = "login.invalid_destination";
break; case "relying_party_registration_not_found" ->
case "access_denied": errorOAuth = "login.relyingPartyRegistrationNotFound";
erroroauth = "login.oauth2AccessDenied";
break;
case "invalid_user_info_response":
erroroauth = "login.oauth2InvalidUserInfoResponse";
break;
case "invalid_request":
erroroauth = "login.oauth2invalidRequest";
break;
case "invalid_id_token":
erroroauth = "login.oauth2InvalidIdToken";
break;
case "oauth2_admin_blocked_user":
erroroauth = "login.oauth2AdminBlockedUser";
break;
case "userIsDisabled":
erroroauth = "login.userIsDisabled";
break;
case "invalid_destination":
erroroauth = "login.invalid_destination";
break;
case "relying_party_registration_not_found":
erroroauth = "login.relyingPartyRegistrationNotFound";
break;
// Valid InResponseTo was not available from the validation context, unable to // Valid InResponseTo was not available from the validation context, unable to
// evaluate // evaluate
case "invalid_in_response_to": case "invalid_in_response_to" -> errorOAuth = "login.invalid_in_response_to";
erroroauth = "login.invalid_in_response_to"; case "not_authentication_provider_found" ->
break; errorOAuth = "login.not_authentication_provider_found";
case "not_authentication_provider_found":
erroroauth = "login.not_authentication_provider_found";
break;
default:
break;
} }
model.addAttribute("erroroauth", erroroauth);
model.addAttribute("errorOAuth", errorOAuth);
} }
if (request.getParameter("messageType") != null) { if (request.getParameter("messageType") != null) {
model.addAttribute("messageType", "changedCredsMessage"); model.addAttribute("messageType", "changedCredsMessage");
} }
if (request.getParameter("logout") != null) { if (request.getParameter("logout") != null) {
model.addAttribute("logoutMessage", "You have been logged out."); model.addAttribute("logoutMessage", "You have been logged out.");
} }
return "login"; return "login";
} }
@@ -230,13 +236,11 @@ public class AccountWebController {
.plus(maxInactiveInterval, ChronoUnit.SECONDS); .plus(maxInactiveInterval, ChronoUnit.SECONDS);
if (now.isAfter(expirationTime)) { if (now.isAfter(expirationTime)) {
sessionPersistentRegistry.expireSession(sessionEntity.getSessionId()); sessionPersistentRegistry.expireSession(sessionEntity.getSessionId());
hasActiveSession = false;
} else { } else {
hasActiveSession = !sessionEntity.isExpired(); hasActiveSession = !sessionEntity.isExpired();
} }
lastRequest = sessionEntity.getLastRequest(); lastRequest = sessionEntity.getLastRequest();
} else { } else {
hasActiveSession = false;
// No session, set default last request time // No session, set default last request time
lastRequest = new Date(0); lastRequest = new Date(0);
} }
@@ -273,53 +277,41 @@ public class AccountWebController {
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
String messageType = request.getParameter("messageType"); String messageType = request.getParameter("messageType");
String deleteMessage = null;
String deleteMessage;
if (messageType != null) { if (messageType != null) {
switch (messageType) { deleteMessage =
case "deleteCurrentUser": switch (messageType) {
deleteMessage = "deleteCurrentUserMessage"; case "deleteCurrentUser" -> "deleteCurrentUserMessage";
break; case "deleteUsernameExists" -> "deleteUsernameExistsMessage";
case "deleteUsernameExists": default -> null;
deleteMessage = "deleteUsernameExistsMessage"; };
break;
default:
break;
}
model.addAttribute("deleteMessage", deleteMessage); model.addAttribute("deleteMessage", deleteMessage);
String addMessage = null;
switch (messageType) { String addMessage;
case "usernameExists": addMessage =
addMessage = "usernameExistsMessage"; switch (messageType) {
break; case "usernameExists" -> "usernameExistsMessage";
case "invalidUsername": case "invalidUsername" -> "invalidUsernameMessage";
addMessage = "invalidUsernameMessage"; case "invalidPassword" -> "invalidPasswordMessage";
break; default -> null;
case "invalidPassword": };
addMessage = "invalidPasswordMessage";
break;
default:
break;
}
model.addAttribute("addMessage", addMessage); model.addAttribute("addMessage", addMessage);
} }
String changeMessage = null;
String changeMessage;
if (messageType != null) { if (messageType != null) {
switch (messageType) { changeMessage =
case "userNotFound": switch (messageType) {
changeMessage = "userNotFoundMessage"; case "userNotFound" -> "userNotFoundMessage";
break; case "downgradeCurrentUser" -> "downgradeCurrentUserMessage";
case "downgradeCurrentUser": case "disabledCurrentUser" -> "disabledCurrentUserMessage";
changeMessage = "downgradeCurrentUserMessage"; default -> messageType;
break; };
case "disabledCurrentUser":
changeMessage = "disabledCurrentUserMessage";
break;
default:
changeMessage = messageType;
break;
}
model.addAttribute("changeMessage", changeMessage); model.addAttribute("changeMessage", changeMessage);
} }
model.addAttribute("users", sortedUsers); model.addAttribute("users", sortedUsers);
model.addAttribute("currentUsername", authentication.getName()); model.addAttribute("currentUsername", authentication.getName());
model.addAttribute("roleDetails", roleDetails); model.addAttribute("roleDetails", roleDetails);
@@ -340,78 +332,51 @@ public class AccountWebController {
if (authentication != null && authentication.isAuthenticated()) { if (authentication != null && authentication.isAuthenticated()) {
Object principal = authentication.getPrincipal(); Object principal = authentication.getPrincipal();
String username = null; String username = null;
if (principal instanceof UserDetails) {
// Cast the principal object to UserDetails // Retrieve username and other attributes and add login attributes to the model
UserDetails userDetails = (UserDetails) principal; if (principal instanceof UserDetails userDetails) {
// Retrieve username and other attributes
username = userDetails.getUsername(); username = userDetails.getUsername();
// Add oAuth2 Login attributes to the model
model.addAttribute("oAuth2Login", false); model.addAttribute("oAuth2Login", false);
} }
if (principal instanceof OAuth2User) { if (principal instanceof OAuth2User userDetails) {
// Cast the principal object to OAuth2User username = userDetails.getName();
OAuth2User userDetails = (OAuth2User) principal;
// Retrieve username and other attributes
username =
userDetails.getAttribute(
applicationProperties.getSecurity().getOauth2().getUseAsUsername());
// Add oAuth2 Login attributes to the model
model.addAttribute("oAuth2Login", true); model.addAttribute("oAuth2Login", true);
} }
if (principal instanceof CustomSaml2AuthenticatedPrincipal) { if (principal instanceof CustomSaml2AuthenticatedPrincipal userDetails) {
// Cast the principal object to OAuth2User username = userDetails.name();
CustomSaml2AuthenticatedPrincipal userDetails = model.addAttribute("saml2Login", true);
(CustomSaml2AuthenticatedPrincipal) principal;
// Retrieve username and other attributes
username = userDetails.getName();
// Add oAuth2 Login attributes to the model
model.addAttribute("oAuth2Login", true);
} }
if (username != null) { if (username != null) {
// Fetch user details from the database // Fetch user details from the database
Optional<User> user = Optional<User> user = userRepository.findByUsernameIgnoreCaseWithSettings(username);
userRepository
.findByUsernameIgnoreCaseWithSettings( // Assuming findByUsername if (user.isEmpty()) {
// method exists
username);
if (!user.isPresent()) {
return "redirect:/error"; return "redirect:/error";
} }
// Convert settings map to JSON string // Convert settings map to JSON string
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
String settingsJson; String settingsJson;
try { try {
settingsJson = objectMapper.writeValueAsString(user.get().getSettings()); settingsJson = objectMapper.writeValueAsString(user.get().getSettings());
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
// Handle JSON conversion error log.error("Error converting settings map", e);
log.error("exception", e);
return "redirect:/error"; return "redirect:/error";
} }
String messageType = request.getParameter("messageType"); String messageType = request.getParameter("messageType");
if (messageType != null) { if (messageType != null) {
switch (messageType) { switch (messageType) {
case "notAuthenticated": case "notAuthenticated" -> messageType = "notAuthenticatedMessage";
messageType = "notAuthenticatedMessage"; case "userNotFound" -> messageType = "userNotFoundMessage";
break; case "incorrectPassword" -> messageType = "incorrectPasswordMessage";
case "userNotFound": case "usernameExists" -> messageType = "usernameExistsMessage";
messageType = "userNotFoundMessage"; case "invalidUsername" -> messageType = "invalidUsernameMessage";
break;
case "incorrectPassword":
messageType = "incorrectPasswordMessage";
break;
case "usernameExists":
messageType = "usernameExistsMessage";
break;
case "invalidUsername":
messageType = "invalidUsernameMessage";
break;
default:
break;
} }
model.addAttribute("messageType", messageType);
} }
// Add attributes to the model
model.addAttribute("username", username); model.addAttribute("username", username);
model.addAttribute("messageType", messageType);
model.addAttribute("role", user.get().getRolesAsString()); model.addAttribute("role", user.get().getRolesAsString());
model.addAttribute("settings", settingsJson); model.addAttribute("settings", settingsJson);
model.addAttribute("changeCredsFlag", user.get().isFirstLogin()); model.addAttribute("changeCredsFlag", user.get().isFirstLogin());
@@ -432,19 +397,12 @@ public class AccountWebController {
} }
if (authentication != null && authentication.isAuthenticated()) { if (authentication != null && authentication.isAuthenticated()) {
Object principal = authentication.getPrincipal(); Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) { if (principal instanceof UserDetails userDetails) {
// Cast the principal object to UserDetails
UserDetails userDetails = (UserDetails) principal;
// Retrieve username and other attributes
String username = userDetails.getUsername(); String username = userDetails.getUsername();
// Fetch user details from the database // Fetch user details from the database
Optional<User> user = Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
userRepository if (user.isEmpty()) {
.findByUsernameIgnoreCase( // Assuming findByUsername method exists // Handle error appropriately, example redirection in case of error
username);
if (!user.isPresent()) {
// Handle error appropriately
// Example redirection in case of error
return "redirect:/error"; return "redirect:/error";
} }
String messageType = request.getParameter("messageType"); String messageType = request.getParameter("messageType");
@@ -467,7 +425,7 @@ public class AccountWebController {
} }
model.addAttribute("messageType", messageType); model.addAttribute("messageType", messageType);
} }
// Add attributes to the model
model.addAttribute("username", username); model.addAttribute("username", username);
} }
} else { } else {

View File

@@ -1,5 +1,7 @@
package stirling.software.SPDF.model; package stirling.software.SPDF.model;
import static stirling.software.SPDF.utils.validation.Validator.*;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
@@ -12,7 +14,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -34,10 +35,11 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig; import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.config.YamlPropertySourceFactory; import stirling.software.SPDF.config.YamlPropertySourceFactory;
import stirling.software.SPDF.model.provider.GithubProvider; import stirling.software.SPDF.model.exception.UnsupportedProviderException;
import stirling.software.SPDF.model.provider.GitHubProvider;
import stirling.software.SPDF.model.provider.GoogleProvider; import stirling.software.SPDF.model.provider.GoogleProvider;
import stirling.software.SPDF.model.provider.KeycloakProvider; import stirling.software.SPDF.model.provider.KeycloakProvider;
import stirling.software.SPDF.model.provider.UnsupportedProviderException; import stirling.software.SPDF.model.provider.Provider;
@Configuration @Configuration
@ConfigurationProperties(prefix = "") @ConfigurationProperties(prefix = "")
@@ -136,13 +138,13 @@ public class ApplicationProperties {
|| loginMethod.equalsIgnoreCase(LoginMethods.ALL.toString())); || loginMethod.equalsIgnoreCase(LoginMethods.ALL.toString()));
} }
public boolean isOauth2Activ() { public boolean isOauth2Active() {
return (oauth2 != null return (oauth2 != null
&& oauth2.getEnabled() && oauth2.getEnabled()
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString())); && !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
} }
public boolean isSaml2Activ() { public boolean isSaml2Active() {
return (saml2 != null return (saml2 != null
&& saml2.getEnabled() && saml2.getEnabled()
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString())); && !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
@@ -158,6 +160,7 @@ public class ApplicationProperties {
@Setter @Setter
@ToString @ToString
public static class SAML2 { public static class SAML2 {
private String provider;
private Boolean enabled = false; private Boolean enabled = false;
private Boolean autoCreateUser = false; private Boolean autoCreateUser = false;
private Boolean blockRegistration = false; private Boolean blockRegistration = false;
@@ -195,7 +198,7 @@ public class ApplicationProperties {
} }
} }
public Resource getidpCert() { public Resource getIdpCert() {
if (idpCert == null) return null; if (idpCert == null) return null;
if (idpCert.startsWith("classpath:")) { if (idpCert.startsWith("classpath:")) {
return new ClassPathResource(idpCert.substring("classpath:".length())); return new ClassPathResource(idpCert.substring("classpath:".length()));
@@ -225,12 +228,11 @@ public class ApplicationProperties {
private Collection<String> scopes = new ArrayList<>(); private Collection<String> scopes = new ArrayList<>();
private String provider; private String provider;
private Client client = new Client(); private Client client = new Client();
private String logoutUrl;
public void setScopes(String scopes) { public void setScopes(String scopes) {
List<String> scopesList = List<String> scopesList =
Arrays.stream(scopes.split(",")) Arrays.stream(scopes.split(",")).map(String::trim).toList();
.map(String::trim)
.collect(Collectors.toList());
this.scopes.addAll(scopesList); this.scopes.addAll(scopesList);
} }
@@ -243,32 +245,31 @@ public class ApplicationProperties {
} }
public boolean isSettingsValid() { public boolean isSettingsValid() {
return isValid(this.getIssuer(), "issuer") return !isStringEmpty(this.getIssuer())
&& isValid(this.getClientId(), "clientId") && !isStringEmpty(this.getClientId())
&& isValid(this.getClientSecret(), "clientSecret") && !isStringEmpty(this.getClientSecret())
&& isValid(this.getScopes(), "scopes") && !isCollectionEmpty(this.getScopes())
&& isValid(this.getUseAsUsername(), "useAsUsername"); && !isStringEmpty(this.getUseAsUsername());
} }
@Data @Data
public static class Client { public static class Client {
private GoogleProvider google = new GoogleProvider(); private GoogleProvider google = new GoogleProvider();
private GithubProvider github = new GithubProvider(); private GitHubProvider github = new GitHubProvider();
private KeycloakProvider keycloak = new KeycloakProvider(); private KeycloakProvider keycloak = new KeycloakProvider();
public Provider get(String registrationId) throws UnsupportedProviderException { public Provider get(String registrationId) throws UnsupportedProviderException {
switch (registrationId.toLowerCase()) { return switch (registrationId.toLowerCase()) {
case "google": case "google" -> getGoogle();
return getGoogle(); case "github" -> getGithub();
case "github": case "keycloak" -> getKeycloak();
return getGithub(); default ->
case "keycloak": throw new UnsupportedProviderException(
return getKeycloak(); "Logout from the provider "
default: + registrationId
throw new UnsupportedProviderException( + " is not supported. "
"Logout from the provider is not supported? Report it at" + "Report it at https://github.com/Stirling-Tools/Stirling-PDF/issues");
+ " https://github.com/Stirling-Tools/Stirling-PDF/issues"); };
}
} }
} }
} }
@@ -335,10 +336,10 @@ public class ApplicationProperties {
@Override @Override
public String toString() { public String toString() {
return """ return """
Driver { Driver {
driverName='%s' driverName='%s'
} }
""" """
.formatted(driverName); .formatted(driverName);
} }
} }

View File

@@ -1,80 +0,0 @@
package stirling.software.SPDF.model;
import java.util.Collection;
public class Provider implements ProviderInterface {
private String name;
private String clientName;
public String getName() {
return name;
}
public String getClientName() {
return clientName;
}
protected boolean isValid(String value, String name) {
if (value != null && !value.trim().isEmpty()) {
return true;
}
return false;
}
protected boolean isValid(Collection<String> value, String name) {
if (value != null && !value.isEmpty()) {
return true;
}
return false;
}
@Override
public Collection<String> getScopes() {
throw new UnsupportedOperationException("Unimplemented method 'getScope'");
}
@Override
public void setScopes(String scopes) {
throw new UnsupportedOperationException("Unimplemented method 'setScope'");
}
@Override
public String getUseAsUsername() {
throw new UnsupportedOperationException("Unimplemented method 'getUseAsUsername'");
}
@Override
public void setUseAsUsername(String useAsUsername) {
throw new UnsupportedOperationException("Unimplemented method 'setUseAsUsername'");
}
@Override
public String getIssuer() {
throw new UnsupportedOperationException("Unimplemented method 'getIssuer'");
}
@Override
public void setIssuer(String issuer) {
throw new UnsupportedOperationException("Unimplemented method 'setIssuer'");
}
@Override
public String getClientSecret() {
throw new UnsupportedOperationException("Unimplemented method 'getClientSecret'");
}
@Override
public void setClientSecret(String clientSecret) {
throw new UnsupportedOperationException("Unimplemented method 'setClientSecret'");
}
@Override
public String getClientId() {
throw new UnsupportedOperationException("Unimplemented method 'getClientId'");
}
@Override
public void setClientId(String clientId) {
throw new UnsupportedOperationException("Unimplemented method 'setClientId'");
}
}

View File

@@ -1,26 +0,0 @@
package stirling.software.SPDF.model;
import java.util.Collection;
public interface ProviderInterface {
public Collection<String> getScopes();
public void setScopes(String scopes);
public String getUseAsUsername();
public void setUseAsUsername(String useAsUsername);
public String getIssuer();
public void setIssuer(String issuer);
public String getClientSecret();
public void setClientSecret(String clientSecret);
public String getClientId();
public void setClientId(String clientId);
}

View File

@@ -0,0 +1,24 @@
package stirling.software.SPDF.model;
import lombok.Getter;
@Getter
public enum UsernameAttribute {
EMAIL("email"),
LOGIN("login"),
PROFILE("profile"),
NAME("name"),
USERNAME("username"),
NICKNAME("nickname"),
GIVEN_NAME("given_name"),
MIDDLE_NAME("middle_name"),
FAMILY_NAME("family_name"),
PREFERRED_NAME("preferred_name"),
PREFERRED_USERNAME("preferred_username");
private final String name;
UsernameAttribute(final String name) {
this.name = name;
}
}

View File

@@ -30,4 +30,13 @@ public class OptimizePdfRequest extends PDFFile {
"Whether to normalize the PDF content for better compatibility. Default is false.", "Whether to normalize the PDF content for better compatibility. Default is false.",
defaultValue = "false") defaultValue = "false")
private Boolean normalize = false; private Boolean normalize = false;
@Schema(
description = "Whether to convert the PDF to grayscale. Default is false.",
defaultValue = "false")
private Boolean grayscale = false;
public Boolean getGrayscale() {
return grayscale;
}
} }

View File

@@ -0,0 +1,11 @@
package stirling.software.SPDF.model.exception;
public class NoProviderFoundException extends Exception {
public NoProviderFoundException(String message) {
super(message);
}
public NoProviderFoundException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,4 +1,4 @@
package stirling.software.SPDF.model.provider; package stirling.software.SPDF.model.exception;
public class UnsupportedProviderException extends Exception { public class UnsupportedProviderException extends Exception {
public UnsupportedProviderException(String message) { public UnsupportedProviderException(String message) {

View File

@@ -0,0 +1,7 @@
package stirling.software.SPDF.model.exception;
public class UnsupportedUsernameAttribute extends RuntimeException {
public UnsupportedUsernameAttribute(String message) {
super(message);
}
}

View File

@@ -0,0 +1,86 @@
package stirling.software.SPDF.model.provider;
import java.util.ArrayList;
import java.util.Collection;
import lombok.NoArgsConstructor;
import stirling.software.SPDF.model.UsernameAttribute;
@NoArgsConstructor
public class GitHubProvider extends Provider {
private static final String NAME = "github";
private static final String CLIENT_NAME = "GitHub";
private static final String AUTHORIZATION_URI = "https://github.com/login/oauth/authorize";
private static final String TOKEN_URI = "https://github.com/login/oauth/access_token";
private static final String USER_INFO_URI = "https://api.github.com/user";
public GitHubProvider(
String clientId,
String clientSecret,
Collection<String> scopes,
UsernameAttribute useAsUsername) {
super(
null,
NAME,
CLIENT_NAME,
clientId,
clientSecret,
scopes,
useAsUsername != null ? useAsUsername : UsernameAttribute.LOGIN,
null,
AUTHORIZATION_URI,
TOKEN_URI,
USER_INFO_URI);
}
@Override
public String getAuthorizationUri() {
return AUTHORIZATION_URI;
}
@Override
public String getTokenUri() {
return TOKEN_URI;
}
@Override
public String getUserInfoUri() {
return USER_INFO_URI;
}
@Override
public String getName() {
return NAME;
}
@Override
public String getClientName() {
return CLIENT_NAME;
}
@Override
public Collection<String> getScopes() {
Collection<String> scopes = super.getScopes();
if (scopes == null || scopes.isEmpty()) {
scopes = new ArrayList<>();
scopes.add("read:user");
}
return scopes;
}
@Override
public String toString() {
return "GitHub [clientId="
+ getClientId()
+ ", clientSecret="
+ (getClientSecret() != null && !getClientSecret().isEmpty() ? "*****" : "NULL")
+ ", scopes="
+ getScopes()
+ ", useAsUsername="
+ getUseAsUsername()
+ "]";
}
}

View File

@@ -1,114 +0,0 @@
package stirling.software.SPDF.model.provider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
import stirling.software.SPDF.model.Provider;
public class GithubProvider extends Provider {
private static final String authorizationUri = "https://github.com/login/oauth/authorize";
private static final String tokenUri = "https://github.com/login/oauth/access_token";
private static final String userInfoUri = "https://api.github.com/user";
private String clientId;
private String clientSecret;
private Collection<String> scopes = new ArrayList<>();
private String useAsUsername = "login";
public String getAuthorizationuri() {
return authorizationUri;
}
public String getTokenuri() {
return tokenUri;
}
public String getUserinfouri() {
return userInfoUri;
}
@Override
public String getIssuer() {
return new String();
}
@Override
public void setIssuer(String issuer) {}
@Override
public String getClientId() {
return this.clientId;
}
@Override
public void setClientId(String clientId) {
this.clientId = clientId;
}
@Override
public String getClientSecret() {
return this.clientSecret;
}
@Override
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
@Override
public Collection<String> getScopes() {
if (scopes == null || scopes.isEmpty()) {
scopes = new ArrayList<>();
scopes.add("read:user");
}
return scopes;
}
@Override
public void setScopes(String scopes) {
this.scopes =
Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList());
}
@Override
public String getUseAsUsername() {
return this.useAsUsername;
}
@Override
public void setUseAsUsername(String useAsUsername) {
this.useAsUsername = useAsUsername;
}
@Override
public String toString() {
return "GitHub [clientId="
+ clientId
+ ", clientSecret="
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
+ ", scopes="
+ scopes
+ ", useAsUsername="
+ useAsUsername
+ "]";
}
@Override
public String getName() {
return "github";
}
@Override
public String getClientName() {
return "GitHub";
}
public boolean isSettingsValid() {
return super.isValid(this.getClientId(), "clientId")
&& super.isValid(this.getClientSecret(), "clientSecret")
&& super.isValid(this.getScopes(), "scopes")
&& isValid(this.getUseAsUsername(), "useAsUsername");
}
}

View File

@@ -1,116 +1,85 @@
package stirling.software.SPDF.model.provider; package stirling.software.SPDF.model.provider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.stream.Collectors;
import stirling.software.SPDF.model.Provider; import lombok.NoArgsConstructor;
import stirling.software.SPDF.model.UsernameAttribute;
@NoArgsConstructor
public class GoogleProvider extends Provider { public class GoogleProvider extends Provider {
private static final String authorizationUri = "https://accounts.google.com/o/oauth2/v2/auth"; private static final String NAME = "google";
private static final String tokenUri = "https://www.googleapis.com/oauth2/v4/token"; private static final String CLIENT_NAME = "Google";
private static final String userInfoUri = private static final String AUTHORIZATION_URI = "https://accounts.google.com/o/oauth2/v2/auth";
private static final String TOKEN_URI = "https://www.googleapis.com/oauth2/v4/token";
private static final String USER_INFO_URI =
"https://www.googleapis.com/oauth2/v3/userinfo?alt=json"; "https://www.googleapis.com/oauth2/v3/userinfo?alt=json";
private String clientId;
private String clientSecret;
private Collection<String> scopes = new ArrayList<>();
private String useAsUsername = "email";
public String getAuthorizationuri() { public GoogleProvider(
return authorizationUri; String clientId,
String clientSecret,
Collection<String> scopes,
UsernameAttribute useAsUsername) {
super(
null,
NAME,
CLIENT_NAME,
clientId,
clientSecret,
scopes,
useAsUsername,
null,
AUTHORIZATION_URI,
TOKEN_URI,
USER_INFO_URI);
} }
public String getTokenuri() { public String getAuthorizationUri() {
return tokenUri; return AUTHORIZATION_URI;
} }
public String getUserinfouri() { public String getTokenUri() {
return userInfoUri; return TOKEN_URI;
}
public String getUserinfoUri() {
return USER_INFO_URI;
} }
@Override @Override
public String getIssuer() { public String getName() {
return new String(); return NAME;
} }
@Override @Override
public void setIssuer(String issuer) {} public String getClientName() {
return CLIENT_NAME;
@Override
public String getClientId() {
return this.clientId;
}
@Override
public void setClientId(String clientId) {
this.clientId = clientId;
}
@Override
public String getClientSecret() {
return this.clientSecret;
}
@Override
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
} }
@Override @Override
public Collection<String> getScopes() { public Collection<String> getScopes() {
Collection<String> scopes = super.getScopes();
if (scopes == null || scopes.isEmpty()) { if (scopes == null || scopes.isEmpty()) {
scopes = new ArrayList<>(); scopes = new ArrayList<>();
scopes.add("https://www.googleapis.com/auth/userinfo.email"); scopes.add("https://www.googleapis.com/auth/userinfo.email");
scopes.add("https://www.googleapis.com/auth/userinfo.profile"); scopes.add("https://www.googleapis.com/auth/userinfo.profile");
} }
return scopes; return scopes;
} }
@Override
public void setScopes(String scopes) {
this.scopes =
Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList());
}
@Override
public String getUseAsUsername() {
return this.useAsUsername;
}
@Override
public void setUseAsUsername(String useAsUsername) {
this.useAsUsername = useAsUsername;
}
@Override @Override
public String toString() { public String toString() {
return "Google [clientId=" return "Google [clientId="
+ clientId + getClientId()
+ ", clientSecret=" + ", clientSecret="
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL") + (getClientSecret() != null && !getClientSecret().isEmpty() ? "*****" : "NULL")
+ ", scopes=" + ", scopes="
+ scopes + getScopes()
+ ", useAsUsername=" + ", useAsUsername="
+ useAsUsername + getUseAsUsername()
+ "]"; + "]";
} }
@Override
public String getName() {
return "google";
}
@Override
public String getClientName() {
return "Google";
}
public boolean isSettingsValid() {
return super.isValid(this.getClientId(), "clientId")
&& super.isValid(this.getClientSecret(), "clientSecret")
&& super.isValid(this.getScopes(), "scopes")
&& isValid(this.getUseAsUsername(), "useAsUsername");
}
} }

View File

@@ -1,106 +1,72 @@
package stirling.software.SPDF.model.provider; package stirling.software.SPDF.model.provider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.stream.Collectors;
import stirling.software.SPDF.model.Provider; import lombok.NoArgsConstructor;
import stirling.software.SPDF.model.UsernameAttribute;
@NoArgsConstructor
public class KeycloakProvider extends Provider { public class KeycloakProvider extends Provider {
private String issuer; private static final String NAME = "keycloak";
private String clientId; private static final String CLIENT_NAME = "Keycloak";
private String clientSecret;
private Collection<String> scopes = new ArrayList<>();
private String useAsUsername = "email";
@Override public KeycloakProvider(
public String getIssuer() { String issuer,
return this.issuer; String clientId,
String clientSecret,
Collection<String> scopes,
UsernameAttribute useAsUsername) {
super(
issuer,
NAME,
CLIENT_NAME,
clientId,
clientSecret,
scopes,
useAsUsername,
null,
null,
null,
null);
} }
@Override @Override
public void setIssuer(String issuer) { public String getName() {
this.issuer = issuer; return NAME;
} }
@Override @Override
public String getClientId() { public String getClientName() {
return this.clientId; return CLIENT_NAME;
}
@Override
public void setClientId(String clientId) {
this.clientId = clientId;
}
@Override
public String getClientSecret() {
return this.clientSecret;
}
@Override
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
} }
@Override @Override
public Collection<String> getScopes() { public Collection<String> getScopes() {
Collection<String> scopes = super.getScopes();
if (scopes == null || scopes.isEmpty()) { if (scopes == null || scopes.isEmpty()) {
scopes = new ArrayList<>(); scopes = new ArrayList<>();
scopes.add("profile"); scopes.add("profile");
scopes.add("email"); scopes.add("email");
} }
return scopes; return scopes;
} }
@Override
public void setScopes(String scopes) {
this.scopes =
Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList());
}
@Override
public String getUseAsUsername() {
return this.useAsUsername;
}
@Override
public void setUseAsUsername(String useAsUsername) {
this.useAsUsername = useAsUsername;
}
@Override @Override
public String toString() { public String toString() {
return "Keycloak [issuer=" return "Keycloak [issuer="
+ issuer + getIssuer()
+ ", clientId=" + ", clientId="
+ clientId + getClientId()
+ ", clientSecret=" + ", clientSecret="
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL") + (getClientSecret() != null && !getClientSecret().isBlank() ? "*****" : "NULL")
+ ", scopes=" + ", scopes="
+ scopes + getScopes()
+ ", useAsUsername=" + ", useAsUsername="
+ useAsUsername + getUseAsUsername()
+ "]"; + "]";
} }
@Override
public String getName() {
return "keycloak";
}
@Override
public String getClientName() {
return "Keycloak";
}
public boolean isSettingsValid() {
return isValid(this.getIssuer(), "issuer")
&& isValid(this.getClientId(), "clientId")
&& isValid(this.getClientSecret(), "clientSecret")
&& isValid(this.getScopes(), "scopes")
&& isValid(this.getUseAsUsername(), "useAsUsername");
}
} }

View File

@@ -0,0 +1,134 @@
package stirling.software.SPDF.model.provider;
import static stirling.software.SPDF.model.UsernameAttribute.EMAIL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
import lombok.Data;
import lombok.NoArgsConstructor;
import stirling.software.SPDF.model.UsernameAttribute;
import stirling.software.SPDF.model.exception.UnsupportedUsernameAttribute;
@Data
@NoArgsConstructor
public class Provider {
public static final String EXCEPTION_MESSAGE = "The attribute %s is not supported for %s.";
private String issuer;
private String name;
private String clientName;
private String clientId;
private String clientSecret;
private Collection<String> scopes;
private UsernameAttribute useAsUsername;
private String logoutUrl;
private String authorizationUri;
private String tokenUri;
private String userInfoUri;
public Provider(
String issuer,
String name,
String clientName,
String clientId,
String clientSecret,
Collection<String> scopes,
UsernameAttribute useAsUsername,
String logoutUrl,
String authorizationUri,
String tokenUri,
String userInfoUri) {
this.issuer = issuer;
this.name = name;
this.clientName = clientName;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.scopes = scopes == null ? new ArrayList<>() : scopes;
this.useAsUsername =
useAsUsername != null ? validateUsernameAttribute(useAsUsername) : EMAIL;
this.logoutUrl = logoutUrl;
this.authorizationUri = authorizationUri;
this.tokenUri = tokenUri;
this.userInfoUri = userInfoUri;
}
public void setScopes(String scopes) {
if (scopes != null && !scopes.isBlank()) {
this.scopes =
Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList());
}
}
private UsernameAttribute validateUsernameAttribute(UsernameAttribute usernameAttribute) {
switch (name) {
case "google" -> {
return validateGoogleUsernameAttribute(usernameAttribute);
}
case "github" -> {
return validateGitHubUsernameAttribute(usernameAttribute);
}
case "keycloak" -> {
return validateKeycloakUsernameAttribute(usernameAttribute);
}
default -> {
return usernameAttribute;
}
}
}
private UsernameAttribute validateKeycloakUsernameAttribute(
UsernameAttribute usernameAttribute) {
switch (usernameAttribute) {
case EMAIL, NAME, GIVEN_NAME, FAMILY_NAME, PREFERRED_USERNAME -> {
return usernameAttribute;
}
default ->
throw new UnsupportedUsernameAttribute(
String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName));
}
}
private UsernameAttribute validateGoogleUsernameAttribute(UsernameAttribute usernameAttribute) {
switch (usernameAttribute) {
case EMAIL, NAME, GIVEN_NAME, FAMILY_NAME -> {
return usernameAttribute;
}
default ->
throw new UnsupportedUsernameAttribute(
String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName));
}
}
private UsernameAttribute validateGitHubUsernameAttribute(UsernameAttribute usernameAttribute) {
switch (usernameAttribute) {
case LOGIN, EMAIL, NAME -> {
return usernameAttribute;
}
default ->
throw new UnsupportedUsernameAttribute(
String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName));
}
}
@Override
public String toString() {
return "Provider [name="
+ getName()
+ ", clientName="
+ getClientName()
+ ", clientId="
+ getClientId()
+ ", clientSecret="
+ (getClientSecret() != null && !getClientSecret().isEmpty() ? "*****" : "NULL")
+ ", scopes="
+ getScopes()
+ ", useAsUsername="
+ getUseAsUsername()
+ "]";
}
}

View File

@@ -207,8 +207,7 @@ public class PostHogService {
private void addIfNotEmpty(Map<String, Object> map, String key, Object value) { private void addIfNotEmpty(Map<String, Object> map, String key, Object value) {
if (value != null) { if (value != null) {
if (value instanceof String) { if (value instanceof String strValue) {
String strValue = (String) value;
if (!StringUtils.isBlank(strValue)) { if (!StringUtils.isBlank(strValue)) {
map.put(key, strValue.trim()); map.put(key, strValue.trim());
} }

View File

@@ -1,11 +1,12 @@
package stirling.software.SPDF.utils; package stirling.software.SPDF.utils;
import java.io.IOException;
import java.net.ServerSocket;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
public class UrlUtils { public class UrlUtils {
private UrlUtils() {}
public static String getOrigin(HttpServletRequest request) { public static String getOrigin(HttpServletRequest request) {
String scheme = request.getScheme(); // http or https String scheme = request.getScheme(); // http or https
String serverName = request.getServerName(); // localhost String serverName = request.getServerName(); // localhost
@@ -14,4 +15,20 @@ public class UrlUtils {
return scheme + "://" + serverName + ":" + serverPort + contextPath; return scheme + "://" + serverName + ":" + serverPort + contextPath;
} }
public static boolean isPortAvailable(int port) {
try (ServerSocket socket = new ServerSocket(port)) {
return true;
} catch (IOException e) {
return false;
}
}
public static String findAvailablePort(int startPort) {
int port = startPort;
while (!isPortAvailable(port)) {
port++;
}
return String.valueOf(port);
}
} }

View File

@@ -0,0 +1,36 @@
package stirling.software.SPDF.utils.validation;
import java.util.Collection;
import stirling.software.SPDF.model.provider.Provider;
public class Validator {
public static boolean validateProvider(Provider provider) {
if (provider == null) {
return false;
}
if (isStringEmpty(provider.getClientId())) {
return false;
}
if (isStringEmpty(provider.getClientSecret())) {
return false;
}
if (isCollectionEmpty(provider.getScopes())) {
return false;
}
return true;
}
public static boolean isStringEmpty(String input) {
return input == null || input.isBlank();
}
public static boolean isCollectionEmpty(Collection<String> input) {
return input == null || input.isEmpty();
}
}

View File

@@ -572,8 +572,8 @@ login.invalid=اسم المستخدم أو كلمة المرور غير صالح
login.locked=تم قفل حسابك. login.locked=تم قفل حسابك.
login.signinTitle=الرجاء تسجيل الدخول login.signinTitle=الرجاء تسجيل الدخول
login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي
login.oauth2AutoCreateDisabled=تم تعطيل الإنشاء التلقائي لمستخدم OAuth2 login.oAuth2AutoCreateDisabled=تم تعطيل الإنشاء التلقائي لمستخدم OAuth2
login.oauth2AdminBlockedUser=تم حظر تسجيل أو تسجيل دخول المستخدمين غير المسجلين حاليًا. يرجى الاتصال بالمسؤول. login.oAuth2AdminBlockedUser=تم حظر تسجيل أو تسجيل دخول المستخدمين غير المسجلين حاليًا. يرجى الاتصال بالمسؤول.
login.oauth2RequestNotFound=لم يتم العثور على طلب التفويض login.oauth2RequestNotFound=لم يتم العثور على طلب التفويض
login.oauth2InvalidUserInfoResponse=استجابة معلومات المستخدم غير صالحة login.oauth2InvalidUserInfoResponse=استجابة معلومات المستخدم غير صالحة
login.oauth2invalidRequest=طلب غير صالح login.oauth2invalidRequest=طلب غير صالح
@@ -951,6 +951,7 @@ fileToPDF.submit=تحويل إلى PDF
compress.title=ضغط compress.title=ضغط
compress.header=ضغط ملف PDF compress.header=ضغط ملف PDF
compress.credit=تستخدم هذه الخدمة qpdf لضغط / تحسين PDF. compress.credit=تستخدم هذه الخدمة qpdf لضغط / تحسين PDF.
compress.grayscale.label=تطبيق التدرج الرمادي للضغط
compress.selectText.1=الوضع اليدوي - من 1 إلى 5 compress.selectText.1=الوضع اليدوي - من 1 إلى 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=مستوى التحسين: compress.selectText.2=مستوى التحسين:

View File

@@ -572,8 +572,8 @@ login.invalid=Etibarsız istifadəçi adı və ya şifr.
login.locked=Sizin hesabınız kilidlənmişdir. login.locked=Sizin hesabınız kilidlənmişdir.
login.signinTitle=Zəhmət olmasa, daxil olun login.signinTitle=Zəhmət olmasa, daxil olun
login.ssoSignIn=Single Sign-on vasitəsilə daxil olun login.ssoSignIn=Single Sign-on vasitəsilə daxil olun
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create İstifadəçisi Deaktivləşdirilmişdir login.oAuth2AutoCreateDisabled=OAUTH2 Auto-Create İstifadəçisi Deaktivləşdirilmişdir
login.oauth2AdminBlockedUser=Qeydiyyatdan keçməmiş istifadəçilərin qeydiyyatı və daxil olması hal-hazırda bloklanmışdır. Zəhmət olmasa, administratorla əlaqə saxlayın. login.oAuth2AdminBlockedUser=Qeydiyyatdan keçməmiş istifadəçilərin qeydiyyatı və daxil olması hal-hazırda bloklanmışdır. Zəhmət olmasa, administratorla əlaqə saxlayın.
login.oauth2RequestNotFound=Təsdiqlənmə sorğusu tapılmadı login.oauth2RequestNotFound=Təsdiqlənmə sorğusu tapılmadı
login.oauth2InvalidUserInfoResponse=Yanlış İstifadəçi Məlumatı Cavabı login.oauth2InvalidUserInfoResponse=Yanlış İstifadəçi Məlumatı Cavabı
login.oauth2invalidRequest=Etibarsız Sorğu login.oauth2invalidRequest=Etibarsız Sorğu
@@ -951,6 +951,7 @@ fileToPDF.submit=PDF-ə Çevir
compress.title=Sıxışdır compress.title=Sıxışdır
compress.header=PDF-i Sıxışdır compress.header=PDF-i Sıxışdır
compress.credit=Bu servis PDF sıxışdırılması/Optimizasiyası üçün Ghostscript istifadə edir. compress.credit=Bu servis PDF sıxışdırılması/Optimizasiyası üçün Ghostscript istifadə edir.
compress.grayscale.label=Sıxma üçün Boz Rəng Tətbiq Edin
compress.selectText.1=Manual Mod - 1-dən 5-ə compress.selectText.1=Manual Mod - 1-dən 5-ə
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimizasiya səviyyəsi: compress.selectText.2=Optimizasiya səviyyəsi:

View File

@@ -572,8 +572,8 @@ login.invalid=Невалидно потребителско име или пар
login.locked=Вашият акаунт е заключен. login.locked=Вашият акаунт е заключен.
login.signinTitle=Моля впишете се login.signinTitle=Моля впишете се
login.ssoSignIn=Влизане чрез еднократно влизане login.ssoSignIn=Влизане чрез еднократно влизане
login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано login.oAuth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано
login.oauth2AdminBlockedUser=Регистрацията или влизането на нерегистрирани потребители в момента е блокирано. Моля, свържете се с администратора. login.oAuth2AdminBlockedUser=Регистрацията или влизането на нерегистрирани потребители в момента е блокирано. Моля, свържете се с администратора.
login.oauth2RequestNotFound=Заявката за оторизация не е намерена login.oauth2RequestNotFound=Заявката за оторизация не е намерена
login.oauth2InvalidUserInfoResponse=Невалидна информация за потребителя login.oauth2InvalidUserInfoResponse=Невалидна информация за потребителя
login.oauth2invalidRequest=Невалидна заявка login.oauth2invalidRequest=Невалидна заявка
@@ -951,6 +951,7 @@ fileToPDF.submit=Преобразуване към PDF
compress.title=Компресиране compress.title=Компресиране
compress.header=Компресиране на PDF compress.header=Компресиране на PDF
compress.credit=Тази услуга използва qpdf за PDF компресиране/оптимизиране. compress.credit=Тази услуга използва qpdf за PDF компресиране/оптимизиране.
compress.grayscale.label=Приложи сива скала за компресиране
compress.selectText.1=Ръчен режим - от 1 до 5 compress.selectText.1=Ръчен режим - от 1 до 5
compress.selectText.1.1=При нива на оптимизация от 6 до 9, в допълнение към общото компресиране на PDF, резолюцията на изображението се намалява, за да се намали допълнително размерът на файла. По-високите нива водят до по-силна компресия на изображенията (до 50% от оригиналния размер), като се постига по-голямо намаляване на размера, но с потенциална загуба на качество на изображенията. compress.selectText.1.1=При нива на оптимизация от 6 до 9, в допълнение към общото компресиране на PDF, резолюцията на изображението се намалява, за да се намали допълнително размерът на файла. По-високите нива водят до по-силна компресия на изображенията (до 50% от оригиналния размер), като се постига по-голямо намаляване на размера, но с потенциална загуба на качество на изображенията.
compress.selectText.2=Ниво на оптимизация: compress.selectText.2=Ниво на оптимизация:

View File

@@ -572,8 +572,8 @@ login.invalid=Nom d'usuari/contrasenya no vàlid
login.locked=Compte bloquejat login.locked=Compte bloquejat
login.signinTitle=Autenticat login.signinTitle=Autenticat
login.ssoSignIn=Inicia sessió mitjançant inici de sessió únic login.ssoSignIn=Inicia sessió mitjançant inici de sessió únic
login.oauth2AutoCreateDisabled=La creació automàtica d'usuaris OAUTH2 està desactivada login.oAuth2AutoCreateDisabled=La creació automàtica d'usuaris OAUTH2 està desactivada
login.oauth2AdminBlockedUser=El registre o inici de sessió d'usuaris no registrats està actualment bloquejat. Si us plau, contacta amb l'administrador. login.oAuth2AdminBlockedUser=El registre o inici de sessió d'usuaris no registrats està actualment bloquejat. Si us plau, contacta amb l'administrador.
login.oauth2RequestNotFound=Sol·licitud d'autorització no trobada login.oauth2RequestNotFound=Sol·licitud d'autorització no trobada
login.oauth2InvalidUserInfoResponse=Resposta d'informació d'usuari no vàlida login.oauth2InvalidUserInfoResponse=Resposta d'informació d'usuari no vàlida
login.oauth2invalidRequest=Sol·licitud no vàlida login.oauth2invalidRequest=Sol·licitud no vàlida
@@ -951,6 +951,7 @@ fileToPDF.submit=Converteix a PDF
compress.title=Comprimir compress.title=Comprimir
compress.header=Comprimir PDF compress.header=Comprimir PDF
compress.credit=Aquest servei utilitza qpdf per a la compressió/optimització de PDF. compress.credit=Aquest servei utilitza qpdf per a la compressió/optimització de PDF.
compress.grayscale.label=Aplicar escala de grisos per a la compressió
compress.selectText.1=Mode manual: de l'1 al 5 compress.selectText.1=Mode manual: de l'1 al 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Nivell d'optimització: compress.selectText.2=Nivell d'optimització:

View File

@@ -572,8 +572,8 @@ login.invalid=Neplatné uživatelské jméno nebo heslo.
login.locked=Váš účet byl uzamčen. login.locked=Váš účet byl uzamčen.
login.signinTitle=Prosím přihlaste se login.signinTitle=Prosím přihlaste se
login.ssoSignIn=Přihlásit se přes Single Sign-on login.ssoSignIn=Přihlásit se přes Single Sign-on
login.oauth2AutoCreateDisabled=Automatické vytváření OAUTH2 uživatelů je zakázáno login.oAuth2AutoCreateDisabled=Automatické vytváření OAUTH2 uživatelů je zakázáno
login.oauth2AdminBlockedUser=Registrace nebo přihlášení neregistrovaných uživatelů je momentálně blokováno. Kontaktujte prosím správce. login.oAuth2AdminBlockedUser=Registrace nebo přihlášení neregistrovaných uživatelů je momentálně blokováno. Kontaktujte prosím správce.
login.oauth2RequestNotFound=Požadavek na autorizaci nebyl nalezen login.oauth2RequestNotFound=Požadavek na autorizaci nebyl nalezen
login.oauth2InvalidUserInfoResponse=Neplatná odpověď s informacemi o uživateli login.oauth2InvalidUserInfoResponse=Neplatná odpověď s informacemi o uživateli
login.oauth2invalidRequest=Neplatný požadavek login.oauth2invalidRequest=Neplatný požadavek
@@ -951,6 +951,7 @@ fileToPDF.submit=Převést na PDF
compress.title=Komprimovat compress.title=Komprimovat
compress.header=Komprimovat PDF compress.header=Komprimovat PDF
compress.credit=Tato služba používá qpdf pro kompresi/optimalizaci PDF. compress.credit=Tato služba používá qpdf pro kompresi/optimalizaci PDF.
compress.grayscale.label=Použít stupnici šedi pro kompresi
compress.selectText.1=Ruční režim - Od 1 do 5 compress.selectText.1=Ruční režim - Od 1 do 5
compress.selectText.1.1=V úrovních optimalizace 6 až 9 je kromě obecné komprese PDF sníženo rozlišení obrázků pro další zmenšení velikosti souboru. Vyšší úrovně vedou k silnější kompresi obrázků (až na 50 % původní velikosti), čímž dosahují většího zmenšení velikosti, ale s potenciální ztrátou kvality obrázků. compress.selectText.1.1=V úrovních optimalizace 6 až 9 je kromě obecné komprese PDF sníženo rozlišení obrázků pro další zmenšení velikosti souboru. Vyšší úrovně vedou k silnější kompresi obrázků (až na 50 % původní velikosti), čímž dosahují většího zmenšení velikosti, ale s potenciální ztrátou kvality obrázků.
compress.selectText.2=Úroveň optimalizace: compress.selectText.2=Úroveň optimalizace:

View File

@@ -572,8 +572,8 @@ login.invalid=Ugyldigt brugernavn eller adgangskode.
login.locked=Din konto er blevet låst. login.locked=Din konto er blevet låst.
login.signinTitle=Log venligst ind login.signinTitle=Log venligst ind
login.ssoSignIn=Log ind via Single Sign-on login.ssoSignIn=Log ind via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Opret Bruger Deaktiveret login.oAuth2AutoCreateDisabled=OAUTH2 Auto-Opret Bruger Deaktiveret
login.oauth2AdminBlockedUser=Registrering eller login af ikke-registrerede brugere er i øjeblikket blokeret. Kontakt venligst administratoren. login.oAuth2AdminBlockedUser=Registrering eller login af ikke-registrerede brugere er i øjeblikket blokeret. Kontakt venligst administratoren.
login.oauth2RequestNotFound=Autorisationsanmodning ikke fundet login.oauth2RequestNotFound=Autorisationsanmodning ikke fundet
login.oauth2InvalidUserInfoResponse=Ugyldigt Brugerinfo Svar login.oauth2InvalidUserInfoResponse=Ugyldigt Brugerinfo Svar
login.oauth2invalidRequest=Ugyldig Anmodning login.oauth2invalidRequest=Ugyldig Anmodning
@@ -951,6 +951,7 @@ fileToPDF.submit=Konvertér til PDF
compress.title=Komprimer compress.title=Komprimer
compress.header=Komprimer PDF compress.header=Komprimer PDF
compress.credit=Denne tjeneste bruger qpdf til PDF Komprimering/Optimering. compress.credit=Denne tjeneste bruger qpdf til PDF Komprimering/Optimering.
compress.grayscale.label=Anvend gråskala til komprimering
compress.selectText.1=Manuel Tilstand - Fra 1 til 5 compress.selectText.1=Manuel Tilstand - Fra 1 til 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimeringsniveau: compress.selectText.2=Optimeringsniveau:

View File

@@ -572,8 +572,8 @@ login.invalid=Benutzername oder Passwort ungültig.
login.locked=Ihr Konto wurde gesperrt. login.locked=Ihr Konto wurde gesperrt.
login.signinTitle=Bitte melden Sie sich an. login.signinTitle=Bitte melden Sie sich an.
login.ssoSignIn=Anmeldung per Single Sign-On login.ssoSignIn=Anmeldung per Single Sign-On
login.oauth2AutoCreateDisabled=OAUTH2 Benutzer automatisch erstellen deaktiviert login.oAuth2AutoCreateDisabled=OAUTH2 Benutzer automatisch erstellen deaktiviert
login.oauth2AdminBlockedUser=Die Registrierung bzw. das anmelden von nicht registrierten Benutzern ist derzeit gesperrt. Bitte wenden Sie sich an den Administrator. login.oAuth2AdminBlockedUser=Die Registrierung bzw. das anmelden von nicht registrierten Benutzern ist derzeit gesperrt. Bitte wenden Sie sich an den Administrator.
login.oauth2RequestNotFound=Autorisierungsanfrage nicht gefunden login.oauth2RequestNotFound=Autorisierungsanfrage nicht gefunden
login.oauth2InvalidUserInfoResponse=Ungültige Benutzerinformationsantwort login.oauth2InvalidUserInfoResponse=Ungültige Benutzerinformationsantwort
login.oauth2invalidRequest=ungültige Anfrage login.oauth2invalidRequest=ungültige Anfrage
@@ -951,6 +951,7 @@ fileToPDF.submit=In PDF konvertieren
compress.title=Komprimieren compress.title=Komprimieren
compress.header=PDF komprimieren compress.header=PDF komprimieren
compress.credit=Dieser Dienst verwendet qpdf für die PDF-Komprimierung/-Optimierung. compress.credit=Dieser Dienst verwendet qpdf für die PDF-Komprimierung/-Optimierung.
compress.grayscale.label=Graustufen für Komprimierung anwenden
compress.selectText.1=Manueller Modus Von 1 bis 5 compress.selectText.1=Manueller Modus Von 1 bis 5
compress.selectText.1.1=In den Optimierungsstufen 6 bis 9 wird zusätzlich zur allgemeinen PDF-Komprimierung die Bildauflösung reduziert, um die Dateigröße weiter zu verringern. Höhere Stufen führen zu einer stärkeren Bildkomprimierung (bis zu 50 % der Originalgröße), wodurch eine stärkere Größenreduzierung erreicht wird, die jedoch mit einem möglichen Qualitätsverlust der Bilder einhergeht. compress.selectText.1.1=In den Optimierungsstufen 6 bis 9 wird zusätzlich zur allgemeinen PDF-Komprimierung die Bildauflösung reduziert, um die Dateigröße weiter zu verringern. Höhere Stufen führen zu einer stärkeren Bildkomprimierung (bis zu 50 % der Originalgröße), wodurch eine stärkere Größenreduzierung erreicht wird, die jedoch mit einem möglichen Qualitätsverlust der Bilder einhergeht.
compress.selectText.2=Optimierungsstufe: compress.selectText.2=Optimierungsstufe:

View File

@@ -572,8 +572,8 @@ login.invalid=Μη έγκυρο όνομα χρήστη ή κωδικός.
login.locked=Ο λογαριασμός σας έχει κλειδωθεί. login.locked=Ο λογαριασμός σας έχει κλειδωθεί.
login.signinTitle=Παρακαλώ συνδεθείτε login.signinTitle=Παρακαλώ συνδεθείτε
login.ssoSignIn=Σύνδεση μέσω Single Sign-on login.ssoSignIn=Σύνδεση μέσω Single Sign-on
login.oauth2AutoCreateDisabled=Η αυτόματη δημιουργία χρήστη OAUTH2 είναι απενεργοποιημένη login.oAuth2AutoCreateDisabled=Η αυτόματη δημιουργία χρήστη OAUTH2 είναι απενεργοποιημένη
login.oauth2AdminBlockedUser=Η εγγραφή ή σύνδεση μη εγγεγραμμένων χρηστών είναι προς το παρόν αποκλεισμένη. Παρακαλώ επικοινωνήστε με τον διαχειριστή. login.oAuth2AdminBlockedUser=Η εγγραφή ή σύνδεση μη εγγεγραμμένων χρηστών είναι προς το παρόν αποκλεισμένη. Παρακαλώ επικοινωνήστε με τον διαχειριστή.
login.oauth2RequestNotFound=Το αίτημα εξουσιοδότησης δεν βρέθηκε login.oauth2RequestNotFound=Το αίτημα εξουσιοδότησης δεν βρέθηκε
login.oauth2InvalidUserInfoResponse=Μη έγκυρη απόκριση πληροφοριών χρήστη login.oauth2InvalidUserInfoResponse=Μη έγκυρη απόκριση πληροφοριών χρήστη
login.oauth2invalidRequest=Μη έγκυρο αίτημα login.oauth2invalidRequest=Μη έγκυρο αίτημα
@@ -951,6 +951,7 @@ fileToPDF.submit=Μετατροπή σε PDF
compress.title=Συμπίεση compress.title=Συμπίεση
compress.header=Συμπίεση PDF compress.header=Συμπίεση PDF
compress.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf για συμπίεση/βελτιστοποίηση PDF. compress.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf για συμπίεση/βελτιστοποίηση PDF.
compress.grayscale.label=Εφαρμογή κλίμακας του γκρι για συμπίεση
compress.selectText.1=Χειροκίνητη λειτουργία - Από 1 έως 5 compress.selectText.1=Χειροκίνητη λειτουργία - Από 1 έως 5
compress.selectText.1.1=Στα επίπεδα βελτιστοποίησης 6 έως 9, εκτός από τη γενική συμπίεση PDF, η ανάλυση εικόνας μειώνεται για περαιτέρω μείωση του μεγέθους αρχείου. Υψηλότερα επίπεδα οδηγούν σε ισχυρότερη συμπίεση εικόνας (έως και 50% του αρχικού μεγέθους), επιτυγχάνοντας μεγαλύτερη μείωση μεγέθους αλλά με πιθανή απώλεια ποιότητας στις εικόνες. compress.selectText.1.1=Στα επίπεδα βελτιστοποίησης 6 έως 9, εκτός από τη γενική συμπίεση PDF, η ανάλυση εικόνας μειώνεται για περαιτέρω μείωση του μεγέθους αρχείου. Υψηλότερα επίπεδα οδηγούν σε ισχυρότερη συμπίεση εικόνας (έως και 50% του αρχικού μεγέθους), επιτυγχάνοντας μεγαλύτερη μείωση μεγέθους αλλά με πιθανή απώλεια ποιότητας στις εικόνες.
compress.selectText.2=Επίπεδο βελτιστοποίησης: compress.selectText.2=Επίπεδο βελτιστοποίησης:

View File

@@ -572,8 +572,8 @@ login.invalid=Invalid username or password.
login.locked=Your account has been locked. login.locked=Your account has been locked.
login.signinTitle=Please sign in login.signinTitle=Please sign in
login.ssoSignIn=Login via Single Sign-on login.ssoSignIn=Login via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled login.oAuth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator. login.oAuth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
@@ -951,6 +951,7 @@ fileToPDF.submit=Convert to PDF
compress.title=Compress compress.title=Compress
compress.header=Compress PDF compress.header=Compress PDF
compress.credit=This service uses qpdf for PDF Compress/Optimisation. compress.credit=This service uses qpdf for PDF Compress/Optimisation.
compress.grayscale.label=Apply Grayscale for Compression
compress.selectText.1=Manual Mode - From 1 to 5 compress.selectText.1=Manual Mode - From 1 to 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimisation level: compress.selectText.2=Optimisation level:

View File

@@ -572,8 +572,8 @@ login.invalid=Invalid username or password.
login.locked=Your account has been locked. login.locked=Your account has been locked.
login.signinTitle=Please sign in login.signinTitle=Please sign in
login.ssoSignIn=Login via Single Sign-on login.ssoSignIn=Login via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled login.oAuth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator. login.oAuth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
@@ -951,6 +951,7 @@ fileToPDF.submit=Convert to PDF
compress.title=Compress compress.title=Compress
compress.header=Compress PDF compress.header=Compress PDF
compress.credit=This service uses qpdf for PDF Compress/Optimisation. compress.credit=This service uses qpdf for PDF Compress/Optimisation.
compress.grayscale.label=Apply Grayscale for Compression
compress.selectText.1=Manual Mode - From 1 to 5 compress.selectText.1=Manual Mode - From 1 to 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimization level: compress.selectText.2=Optimization level:

View File

@@ -138,13 +138,13 @@ analytics.settings=Puede cambiar la configuración de analíticas en el archivo
# NAVBAR # # NAVBAR #
############# #############
navbar.favorite=Favoritos navbar.favorite=Favoritos
navbar.recent=New and recently updated navbar.recent=Nuevo y recientemente actualizado
navbar.darkmode=Modo oscuro navbar.darkmode=Modo oscuro
navbar.language=Idiomas navbar.language=Idiomas
navbar.settings=Configuración navbar.settings=Configuración
navbar.allTools=Herramientas navbar.allTools=Herramientas
navbar.multiTool=Multi herramientas navbar.multiTool=Multi herramientas
navbar.search=Search navbar.search=Buscar
navbar.sections.organize=Organizar navbar.sections.organize=Organizar
navbar.sections.convertTo=Convertir a PDF navbar.sections.convertTo=Convertir a PDF
navbar.sections.convertFrom=Convertir desde PDF navbar.sections.convertFrom=Convertir desde PDF
@@ -246,14 +246,14 @@ database.info_1=Al importar datos, es fundamental garantizar la estructura corre
database.info_2=El nombre del archivo no importa al cargarlo. Posteriormente se le cambiará el nombre para que siga el formato backup_user_yyyyMMddHHmm.sql, lo que garantiza una convención de nomenclatura coherente. database.info_2=El nombre del archivo no importa al cargarlo. Posteriormente se le cambiará el nombre para que siga el formato backup_user_yyyyMMddHHmm.sql, lo que garantiza una convención de nomenclatura coherente.
database.submit=Importar Copia de Seguridad database.submit=Importar Copia de Seguridad
database.importIntoDatabaseSuccessed=Importación a la base de datos ha sido exitosa database.importIntoDatabaseSuccessed=Importación a la base de datos ha sido exitosa
database.backupCreated=Database backup successful database.backupCreated=Respaldo de la Base de Datos exitoso
database.fileNotFound=Archivo no encontrado database.fileNotFound=Archivo no encontrado
database.fileNullOrEmpty=El archivo no debe ser nulo o vacío. database.fileNullOrEmpty=El archivo no debe ser nulo o vacío.
database.failedImportFile=Archivo de importación fallido database.failedImportFile=Archivo de importación fallido
database.notSupported=This function is not available for your database connection. database.notSupported=Esta función no esta disponible para su conexión de Base de Datos
session.expired=Tu sesión ha caducado. Actualice la página e inténtelo de nuevo. session.expired=Tu sesión ha caducado. Actualice la página e inténtelo de nuevo.
session.refreshPage=Refresh Page session.refreshPage=Refrescar Página
############# #############
# HOME-PAGE # # HOME-PAGE #
@@ -262,18 +262,18 @@ home.desc=Su ventanilla única autohospedada para todas sus necesidades PDF
home.searchBar=Buscar características... home.searchBar=Buscar características...
home.viewPdf.title=View/Edit PDF home.viewPdf.title=Ver/Editar PDF
home.viewPdf.desc=Ver, anotar, añadir texto o imágenes home.viewPdf.desc=Ver, anotar, añadir texto o imágenes
viewPdf.tags=ver,leer,anotar,texto,imagen viewPdf.tags=ver,leer,anotar,texto,imagen
home.setFavorites=Set Favourites home.setFavorites=Agregar Favoritos
home.hideFavorites=Hide Favourites home.hideFavorites=Ocultar Favoritos
home.showFavorites=Show Favourites home.showFavorites=Mostrar Favoritos
home.legacyHomepage=Old homepage home.legacyHomepage=Página de inicio anterior
home.newHomePage=Try our new homepage! home.newHomePage=¡Prueba nuestra nueva página de inicio!
home.alphabetical=Alphabetical home.alphabetical=Alfabetico
home.globalPopularity=Global Popularity home.globalPopularity=Popularidad Global
home.sortBy=Sort by: home.sortBy=Ordenado por:
home.multiTool.title=Multi-herramienta PDF home.multiTool.title=Multi-herramienta PDF
home.multiTool.desc=Combinar, rotar, reorganizar y eliminar páginas home.multiTool.desc=Combinar, rotar, reorganizar y eliminar páginas
@@ -462,9 +462,9 @@ home.MarkdownToPDF.title=Markdown a PDF
home.MarkdownToPDF.desc=Convierte cualquier archivo Markdown a PDF home.MarkdownToPDF.desc=Convierte cualquier archivo Markdown a PDF
MarkdownToPDF.tags=margen,contenido web,transformación,convertir MarkdownToPDF.tags=margen,contenido web,transformación,convertir
home.PDFToMarkdown.title=PDF to Markdown home.PDFToMarkdown.title=PDF a Markdown
home.PDFToMarkdown.desc=Converts any PDF to Markdown home.PDFToMarkdown.desc=Convierte cualquier PDF a Markdown
PDFToMarkdown.tags=markup,web-content,transformation,convert,md PDFToMarkdown.tags=markup,contenido web,transformación,convertir,md
home.getPdfInfo.title=Obtener toda la información en PDF home.getPdfInfo.title=Obtener toda la información en PDF
home.getPdfInfo.desc=Obtiene toda la información posible de archivos PDF home.getPdfInfo.desc=Obtiene toda la información posible de archivos PDF
@@ -489,9 +489,9 @@ home.autoRedact.title=Auto Redactar
home.autoRedact.desc=Redactar automáticamente (ocultar) texto en un PDF según el texto introducido home.autoRedact.desc=Redactar automáticamente (ocultar) texto en un PDF según el texto introducido
autoRedact.tags=Redactar,Ocultar,ocultar,negro,subrayador,oculto autoRedact.tags=Redactar,Ocultar,ocultar,negro,subrayador,oculto
home.redact.title=Manual Redaction home.redact.title=Redacción Manual
home.redact.desc=Redacts a PDF based on selected text, drawn shapes and/or selected page(s) home.redact.desc=Redacta un PDF basado en el texto seleccionado, dibuja formas y/o página(s) selecionada(s)
redact.tags=Redact,Hide,black out,black,marker,hidden,manual redact.tags=Redactar,Ocultar,oscurece,negro,marcador,oculto,manual
home.tableExtraxt.title=PDF a CSV home.tableExtraxt.title=PDF a CSV
home.tableExtraxt.desc=Extraer Tablas de un PDF convirtiéndolas a CSV home.tableExtraxt.desc=Extraer Tablas de un PDF convirtiéndolas a CSV
@@ -533,9 +533,9 @@ home.splitPdfByChapters.title=Dividir PDF por capítulos
home.splitPdfByChapters.desc=Divida un PDF en varios archivos según su estructura de capítulos. home.splitPdfByChapters.desc=Divida un PDF en varios archivos según su estructura de capítulos.
splitPdfByChapters.tags=dividir,capítulos,marcadores,organizar splitPdfByChapters.tags=dividir,capítulos,marcadores,organizar
home.validateSignature.title=Validate PDF Signature home.validateSignature.title=Validar firma del PDF
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents home.validateSignature.desc=Verifica firmas digitales y certificados en los documentos PDF
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate validateSignature.tags=firma,verificar,validar,pdf,certificado,firma digital,validar firma,validar certificado
#replace-invert-color #replace-invert-color
replace-color.title=Reemplazar-Invertir-Color replace-color.title=Reemplazar-Invertir-Color
@@ -572,58 +572,58 @@ login.invalid=Nombre de usuario o contraseña erróneos.
login.locked=Su cuenta se ha bloqueado. login.locked=Su cuenta se ha bloqueado.
login.signinTitle=Por favor, inicie sesión login.signinTitle=Por favor, inicie sesión
login.ssoSignIn=Iniciar sesión a través del inicio de sesión único login.ssoSignIn=Iniciar sesión a través del inicio de sesión único
login.oauth2AutoCreateDisabled=Usuario de creación automática de OAUTH2 DESACTIVADO login.oAuth2AutoCreateDisabled=Usuario de creación automática de OAUTH2 DESACTIVADO
login.oauth2AdminBlockedUser=El registro o inicio de sesión de usuarios no registrados está actualmente bloqueado. Por favor, contáctese con el administrador. login.oAuth2AdminBlockedUser=El registro o inicio de sesión de usuarios no registrados está actualmente bloqueado. Por favor, contáctese con el administrador.
login.oauth2RequestNotFound=Solicitud de autorización no encontrada login.oauth2RequestNotFound=Solicitud de autorización no encontrada
login.oauth2InvalidUserInfoResponse=Respuesta de información de usuario no válida login.oauth2InvalidUserInfoResponse=Respuesta de información de usuario no válida
login.oauth2invalidRequest=Solicitud no válida login.oauth2invalidRequest=Solicitud no válida
login.oauth2AccessDenied=Acceso denegado login.oauth2AccessDenied=Acceso denegado
login.oauth2InvalidTokenResponse=Respuesta de token no válida login.oauth2InvalidTokenResponse=Respuesta de token no válida
login.oauth2InvalidIdToken=Token de identificación no válido login.oauth2InvalidIdToken=Token de identificación no válido
login.relyingPartyRegistrationNotFound=No relying party registration found login.relyingPartyRegistrationNotFound=No hay registro de terceros confiables
login.userIsDisabled=El usuario está desactivado, actualmente el acceso está bloqueado para ese nombre de usuario. Por favor, póngase en contacto con el administrador. login.userIsDisabled=El usuario está desactivado, actualmente el acceso está bloqueado para ese nombre de usuario. Por favor, póngase en contacto con el administrador.
login.alreadyLoggedIn=Ya has iniciado sesión en login.alreadyLoggedIn=Ya has iniciado sesión en
login.alreadyLoggedIn2=dispositivos. Cierra sesión en los dispositivos y vuelve a intentarlo. login.alreadyLoggedIn2=dispositivos. Cierra sesión en los dispositivos y vuelve a intentarlo.
login.toManySessions=Tienes demasiadas sesiones activas login.toManySessions=Tienes demasiadas sesiones activas
#auto-redact #auto-redact
autoRedact.title=Auto Redactar autoRedact.title=Auto Censurar Texto
autoRedact.header=Auto Redactar autoRedact.header=Auto Censurar Texto
autoRedact.colorLabel=Color autoRedact.colorLabel=Color
autoRedact.textsToRedactLabel=Texto para Redactar (separado por líneas) autoRedact.textsToRedactLabel=Texto para Censurar (separado por líneas)
autoRedact.textsToRedactPlaceholder=por ej. \nConfidencial \nAlto-Secreto autoRedact.textsToRedactPlaceholder=por ej. \nConfidencial \nAlto-Secreto
autoRedact.useRegexLabel=Usar Regex autoRedact.useRegexLabel=Usar Regex
autoRedact.wholeWordSearchLabel=Búsqueda por palabra completa autoRedact.wholeWordSearchLabel=Búsqueda por palabra completa
autoRedact.customPaddingLabel=Extra Padding personalizado autoRedact.customPaddingLabel=Extra Padding personalizado
autoRedact.convertPDFToImageLabel=Convertir PDF a imagen PDF (Utilizado para quitar el texto detrás del cajetín) autoRedact.convertPDFToImageLabel=Convertir PDF a imagen PDF (Utilizado para eliminar el texto detrás del cajetín de censura)
autoRedact.submitButton=Enviar autoRedact.submitButton=Enviar
#redact #redact
redact.title=Manual Redaction redact.title=Censurar texto Manualmente
redact.header=Manual Redaction redact.header=Censurar texto Manualmente
redact.submit=Redact redact.submit=Enviar
redact.textBasedRedaction=Text based Redaction redact.textBasedRedaction=Censura basada en texto
redact.pageBasedRedaction=Page-based Redaction redact.pageBasedRedaction=Censura basada en la página
redact.convertPDFToImageLabel=Convert PDF to PDF-Image (Used to remove text behind the box) redact.convertPDFToImageLabel=Convertir PDF a PDF-Image (Utilizado para eliminar el texto detrás del cajetín de censura)
redact.pageRedactionNumbers.title=Pages redact.pageRedactionNumbers.title=Páginas
redact.pageRedactionNumbers.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) redact.pageRedactionNumbers.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
redact.redactionColor.title=Redaction Color redact.redactionColor.title=Color del cajetín
redact.export=Export redact.export=Exportar
redact.upload=Upload redact.upload=Cargar
redact.boxRedaction=Box draw redaction redact.boxRedaction=Dibujar cajetín de censura
redact.zoom=Zoom redact.zoom=Zoom
redact.zoomIn=Zoom in redact.zoomIn=Zoom in
redact.zoomOut=Zoom out redact.zoomOut=Zoom out
redact.nextPage=Next Page redact.nextPage=Siguiente página
redact.previousPage=Previous Page redact.previousPage=Pagina Anterior
redact.toggleSidebar=Toggle Sidebar redact.toggleSidebar=Activar/desactivar barra lateral
redact.showThumbnails=Show Thumbnails redact.showThumbnails=Mostrar Miniaturas
redact.showDocumentOutline=Show Document Outline (double-click to expand/collapse all items) redact.showDocumentOutline=Mostra Esquema del documento (doble-click para expandir/colapsar elementos)
redact.showAttatchments=Show Attachments redact.showAttatchments=Mostrar Adjuntos
redact.showLayers=Show Layers (double-click to reset all layers to the default state) redact.showLayers=Mostrar Capas (doble-click para reiniciar las capas a su estado inicial)
redact.colourPicker=Colour Picker redact.colourPicker=Seleccionador de Color
redact.findCurrentOutlineItem=Find current outline item redact.findCurrentOutlineItem=Buscar elemento actual de esquema
redact.applyChanges=Apply Changes redact.applyChanges=Aplicar Cambios
#showJS #showJS
showJS.title=Mostrar Javascript showJS.title=Mostrar Javascript
@@ -661,9 +661,9 @@ MarkdownToPDF.credit=Usa WeasyPrint
#pdf-to-markdown #pdf-to-markdown
PDFToMarkdown.title=PDF To Markdown PDFToMarkdown.title=PDF a Markdown
PDFToMarkdown.header=PDF To Markdown PDFToMarkdown.header=PDF a Markdown
PDFToMarkdown.submit=Convert PDFToMarkdown.submit=Convertir
#url-to-pdf #url-to-pdf
@@ -713,14 +713,14 @@ AddStampRequest.submit=Enviar
#sanitizePDF #sanitizePDF
sanitizePDF.title=Desinfectar archivo PDF sanitizePDF.title=Limpiar archivo PDF
sanitizePDF.header=Desinfectar un archivo PDF sanitizePDF.header=Limpiar un archivo PDF
sanitizePDF.selectText.1=Eliminar acciones JavaScript sanitizePDF.selectText.1=Eliminar código JavaScript
sanitizePDF.selectText.2=Eliminar archivos incrustados sanitizePDF.selectText.2=Eliminar archivos incrustados
sanitizePDF.selectText.3=Eliminar metadatos sanitizePDF.selectText.3=Eliminar metadatos
sanitizePDF.selectText.4=Eliminar enlaces sanitizePDF.selectText.4=Eliminar enlaces
sanitizePDF.selectText.5=Eliminar fuentes sanitizePDF.selectText.5=Eliminar fuentes
sanitizePDF.submit=Desinfectar PDF sanitizePDF.submit=Limpiar PDF
#addPageNumbers #addPageNumbers
@@ -874,13 +874,13 @@ sign.save=Guardar Firma
sign.personalSigs=Firmas Personales sign.personalSigs=Firmas Personales
sign.sharedSigs=Firmas compartidas sign.sharedSigs=Firmas compartidas
sign.noSavedSigs=No se encontraron firmas guardadas sign.noSavedSigs=No se encontraron firmas guardadas
sign.addToAll=Add to all pages sign.addToAll=Agregar a todas las páginas
sign.delete=Delete sign.delete=Eliminar
sign.first=First page sign.first=Primera página
sign.last=Last page sign.last=Última página
sign.next=Next page sign.next=Siguiente página
sign.previous=Previous page sign.previous=Página anterior
sign.maintainRatio=Toggle maintain aspect ratio sign.maintainRatio=Activar/desactivar la relación de aspecto
#repair #repair
@@ -951,8 +951,9 @@ fileToPDF.submit=Convertir a PDF
compress.title=Comprimir compress.title=Comprimir
compress.header=Comprimir PDF compress.header=Comprimir PDF
compress.credit=Este servicio utiliza qpdf para compresión/optimización de PDF compress.credit=Este servicio utiliza qpdf para compresión/optimización de PDF
compress.grayscale.label=Aplicar escala de grises para compresión
compress.selectText.1=Modo manual - De 1 a 5 compress.selectText.1=Modo manual - De 1 a 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=En los niveles de optimización 6 a 9, además de la compresión general de PDF, se reduce la resolución de la imagen para reducir aún más el tamaño del archivo. Los niveles más altos dan como resultado una mayor compresión de la imagen (hasta el 50 % del tamaño original), lo que permite lograr una mayor reducción del tamaño, pero con una posible pérdida de calidad en las imágenes.
compress.selectText.2=Nivel de optimización: compress.selectText.2=Nivel de optimización:
compress.selectText.3=4 (Terrible para imágenes de texto) compress.selectText.3=4 (Terrible para imágenes de texto)
compress.selectText.4=Modo automático: ajusta automáticamente la calidad para que el PDF tenga el tamaño exacto compress.selectText.4=Modo automático: ajusta automáticamente la calidad para que el PDF tenga el tamaño exacto
@@ -1000,42 +1001,42 @@ pdfOrganiser.placeholder=(por ej., 1,3,2 o 4-8,2,10-12 o 2n-1)
multiTool.title=Multi-herramienta PDF multiTool.title=Multi-herramienta PDF
multiTool.header=Multi-herramienta PDF multiTool.header=Multi-herramienta PDF
multiTool.uploadPrompts=Nombre del archivo multiTool.uploadPrompts=Nombre del archivo
multiTool.selectAll=Select All multiTool.selectAll=Seleccionar Todo
multiTool.deselectAll=Deselect All multiTool.deselectAll=Deseleccionar Todo
multiTool.selectPages=Page Select multiTool.selectPages=Seleccionar página(s)
multiTool.selectedPages=Selected Pages multiTool.selectedPages=Página(s) Selecccionada(s)
multiTool.page=Page multiTool.page=Página
multiTool.deleteSelected=Delete Selected multiTool.deleteSelected=Borrar selecionado(s)
multiTool.downloadAll=Export multiTool.downloadAll=Exportar
multiTool.downloadSelected=Export Selected multiTool.downloadSelected=Exportar selecionado(s)
multiTool.insertPageBreak=Insert Page Break multiTool.insertPageBreak=Insertar salto página
multiTool.addFile=Add File multiTool.addFile=Agregar Archivo
multiTool.rotateLeft=Rotate Left multiTool.rotateLeft=Rotar a la izquierda
multiTool.rotateRight=Rotate Right multiTool.rotateRight=Rotar a la derecha
multiTool.split=Split multiTool.split=Dividir
multiTool.moveLeft=Move Left multiTool.moveLeft=Mover a la izquierda
multiTool.moveRight=Move Right multiTool.moveRight=Mover a la derecha
multiTool.delete=Delete multiTool.delete=Borrar
multiTool.dragDropMessage=Page(s) Selected multiTool.dragDropMessage=Página(s) Selecccionada(s)
multiTool.undo=Undo multiTool.undo=Deshacer
multiTool.redo=Redo multiTool.redo=Rehacer
#decrypt #decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password: decrypt.passwordPrompt=Este archivo está protegido con contraseña. Introdúzca la contraseña:
decrypt.cancelled=Operation cancelled for PDF: {0} decrypt.cancelled=Operación cancelada para el PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0} decrypt.noPassword=No se proporcionó contraseña para PDF cifrado: {0}
decrypt.invalidPassword=Please try again with the correct password. decrypt.invalidPassword=Por favor, inténtelo de nuevo con la contraseña correcta.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0} decrypt.invalidPasswordHeader=Contraseña incorrecta o cifrado no compatible para PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again. decrypt.unexpectedError=Se produjo un error al procesar el archivo. Inténtalo nuevamente.
decrypt.serverError=Server error while decrypting: {0} decrypt.serverError=Error del servidor al descifrar: {0}
decrypt.success=File decrypted successfully. decrypt.success=Archivo descifrado exitosamente.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=Esta función también está disponible en nuestra <a href="{0}">página de herramientas múltiples</a>. ¡Échale un vistazo para ver una interfaz de usuario página por página mejorada y funciones adicionales!
#view pdf #view pdf
viewPdf.title=View/Edit PDF viewPdf.title=Ver/Editar PDF
viewPdf.header=Ver PDF viewPdf.header=Ver PDF
#pageRemover #pageRemover
@@ -1337,50 +1338,50 @@ splitByChapters.submit=Dividir PDF
#File Chooser #File Chooser
fileChooser.click=Click fileChooser.click=Click
fileChooser.or=or fileChooser.or=o
fileChooser.dragAndDrop=Drag & Drop fileChooser.dragAndDrop=Arrastrar & Soltar
fileChooser.dragAndDropPDF=Drag & Drop PDF file fileChooser.dragAndDropPDF=Arrastrar & Soltar archivo PDF
fileChooser.dragAndDropImage=Drag & Drop Image file fileChooser.dragAndDropImage=Arrastrar & Soltar archivo de Imagen
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here fileChooser.hoveredDragAndDrop=Arrastrar & Soltar archivos(s) aquí
#release notes #release notes
releases.footer=Releases releases.footer=Versiones
releases.title=Release Notes releases.title=Notas de la versión
releases.header=Release Notes releases.header=Notas de la versión
releases.current.version=Current Release releases.current.version=versión Actual
releases.note=Release notes are only available in English releases.note=Las notas de la versión solo están disponibles en Inglés
#Validate Signature #Validate Signature
validateSignature.title=Validate PDF Signatures validateSignature.title=Validar firmas del PDF
validateSignature.header=Validate Digital Signatures validateSignature.header=Validar firmas del PDF
validateSignature.selectPDF=Select signed PDF file validateSignature.selectPDF=Seleccione el archivo PDF firmado
validateSignature.submit=Validate Signatures validateSignature.submit=Validar firmas
validateSignature.results=Validation Results validateSignature.results=Resultados de la Validación
validateSignature.status=Status validateSignature.status=Estador
validateSignature.signer=Signer validateSignature.signer=Firmante
validateSignature.date=Date validateSignature.date=Fecha
validateSignature.reason=Reason validateSignature.reason=Razón
validateSignature.location=Location validateSignature.location=Ubicación
validateSignature.noSignatures=No digital signatures found in this document validateSignature.noSignatures=No se encontrario firmas digiales en este documento
validateSignature.status.valid=Valid validateSignature.status.valid=Válido
validateSignature.status.invalid=Invalid validateSignature.status.invalid=Inválido
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity validateSignature.chain.invalid=Error en la validación de la cadena de certificados: no se puede verificar la identidad del firmante
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified validateSignature.trust.invalid=El certificado no se encuentra en los almacenes de confianza: no se puede verificar la fuente
validateSignature.cert.expired=Certificate has expired validateSignature.cert.expired=Certificado expiredo
validateSignature.cert.revoked=Certificate has been revoked validateSignature.cert.revoked=Certificado fue revocado
validateSignature.signature.info=Signature Information validateSignature.signature.info=Información de la firma
validateSignature.signature=Signature validateSignature.signature=Firma
validateSignature.signature.mathValid=Signature is mathematically valid BUT: validateSignature.signature.mathValid=La firma es matemáticamente válida aunque:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional) validateSignature.selectCustomCert=Archivo de certificado personalizado X.509 (opcional)
validateSignature.cert.info=Certificate Details validateSignature.cert.info=Detalles Certificado
validateSignature.cert.issuer=Issuer validateSignature.cert.issuer=Emisor
validateSignature.cert.subject=Subject validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number validateSignature.cert.serialNumber=Número Serie
validateSignature.cert.validFrom=Valid From validateSignature.cert.validFrom=Válido desde
validateSignature.cert.validUntil=Valid Until validateSignature.cert.validUntil=Válido hasta
validateSignature.cert.algorithm=Algorithm validateSignature.cert.algorithm=Algoritmo
validateSignature.cert.keySize=Key Size validateSignature.cert.keySize=Tamaño llave
validateSignature.cert.version=Version validateSignature.cert.version=Versión
validateSignature.cert.keyUsage=Key Usage validateSignature.cert.keyUsage=Uso de la llave
validateSignature.cert.selfSigned=Self-Signed validateSignature.cert.selfSigned=Autofirmado
validateSignature.cert.bits=bits validateSignature.cert.bits=bits

View File

@@ -572,8 +572,8 @@ login.invalid=Okerreko erabiltzaile izena edo pasahitza.
login.locked=Zure kontua blokeatu egin da. login.locked=Zure kontua blokeatu egin da.
login.signinTitle=Mesedez, hasi saioa login.signinTitle=Mesedez, hasi saioa
login.ssoSignIn=Hasi saioa Saioa hasteko modu bakarraren bidez login.ssoSignIn=Hasi saioa Saioa hasteko modu bakarraren bidez
login.oauth2AutoCreateDisabled=OAUTH2 Sortu automatikoki erabiltzailea desgaituta dago login.oAuth2AutoCreateDisabled=OAUTH2 Sortu automatikoki erabiltzailea desgaituta dago
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator. login.oAuth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
@@ -951,6 +951,7 @@ fileToPDF.submit=PDF bihurtu
compress.title=Konprimatu compress.title=Konprimatu
compress.header=PDFa konprimatu compress.header=PDFa konprimatu
compress.credit=Zerbitzu honek qpdf erabiltzen du PDFak komprimatzeko/optimizatzeko compress.credit=Zerbitzu honek qpdf erabiltzen du PDFak komprimatzeko/optimizatzeko
compress.grayscale.label=Aplikatu grisezko eskala konpresiorako
compress.selectText.1=Eskuz 1etik 5ra compress.selectText.1=Eskuz 1etik 5ra
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimizazio maila: compress.selectText.2=Optimizazio maila:

View File

@@ -572,8 +572,8 @@ login.invalid=نام کاربری یا رمز عبور اشتباه است.
login.locked=حساب شما قفل شده است. login.locked=حساب شما قفل شده است.
login.signinTitle=لطفاً وارد شوید login.signinTitle=لطفاً وارد شوید
login.ssoSignIn=ورود از طریق Single Sign-on login.ssoSignIn=ورود از طریق Single Sign-on
login.oauth2AutoCreateDisabled=ایجاد خودکار کاربر با OAUTH2 غیرفعال است login.oAuth2AutoCreateDisabled=ایجاد خودکار کاربر با OAUTH2 غیرفعال است
login.oauth2AdminBlockedUser=ثبت‌نام یا ورود کاربران ثبت‌نشده در حال حاضر مسدود است. لطفاً با مدیر تماس بگیرید. login.oAuth2AdminBlockedUser=ثبت‌نام یا ورود کاربران ثبت‌نشده در حال حاضر مسدود است. لطفاً با مدیر تماس بگیرید.
login.oauth2RequestNotFound=درخواست احراز هویت پیدا نشد login.oauth2RequestNotFound=درخواست احراز هویت پیدا نشد
login.oauth2InvalidUserInfoResponse=پاسخ اطلاعات کاربری نامعتبر است login.oauth2InvalidUserInfoResponse=پاسخ اطلاعات کاربری نامعتبر است
login.oauth2invalidRequest=درخواست نامعتبر login.oauth2invalidRequest=درخواست نامعتبر
@@ -951,6 +951,7 @@ fileToPDF.submit=تبدیل به PDF
compress.title=فشرده‌سازی compress.title=فشرده‌سازی
compress.header=فشرده‌سازی PDF compress.header=فشرده‌سازی PDF
compress.credit=این سرویس از qpdf برای فشرده‌سازی / بهینه‌سازی PDF استفاده می‌کند. compress.credit=این سرویس از qpdf برای فشرده‌سازی / بهینه‌سازی PDF استفاده می‌کند.
compress.grayscale.label=اعمال مقیاس خاکستری برای فشرده‌سازی
compress.selectText.1=حالت دستی - از 1 تا 5 compress.selectText.1=حالت دستی - از 1 تا 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=سطح بهینه‌سازی: compress.selectText.2=سطح بهینه‌سازی:

View File

@@ -572,8 +572,8 @@ login.invalid=Nom d'utilisateur ou mot de passe invalide.
login.locked=Votre compte a été verrouillé. login.locked=Votre compte a été verrouillé.
login.signinTitle=Veuillez vous connecter login.signinTitle=Veuillez vous connecter
login.ssoSignIn=Se connecter via l'authentification unique login.ssoSignIn=Se connecter via l'authentification unique
login.oauth2AutoCreateDisabled=OAUTH2 Création automatique d'utilisateur désactivée login.oAuth2AutoCreateDisabled=OAUTH2 Création automatique d'utilisateur désactivée
login.oauth2AdminBlockedUser=La création ou l'authentification d'utilisateurs non enregistrés est actuellement bloquée. Veuillez contacter l'administrateur. login.oAuth2AdminBlockedUser=La création ou l'authentification d'utilisateurs non enregistrés est actuellement bloquée. Veuillez contacter l'administrateur.
login.oauth2RequestNotFound=Demande d'autorisation introuvable login.oauth2RequestNotFound=Demande d'autorisation introuvable
login.oauth2InvalidUserInfoResponse=Réponse contenant les informations de l'utilisateur est invalide login.oauth2InvalidUserInfoResponse=Réponse contenant les informations de l'utilisateur est invalide
login.oauth2invalidRequest=Requête invalide login.oauth2invalidRequest=Requête invalide
@@ -951,6 +951,7 @@ fileToPDF.submit=Convertir
compress.title=Compresser un PDF compress.title=Compresser un PDF
compress.header=Compresser un PDF (lorsque c'est possible!) compress.header=Compresser un PDF (lorsque c'est possible!)
compress.credit=Ce service utilise qpdf pour la compression et l'optimisation des PDF. compress.credit=Ce service utilise qpdf pour la compression et l'optimisation des PDF.
compress.grayscale.label=Appliquer l'échelle de gris pour la compression
compress.selectText.1=Mode manuel de 1 à 5 compress.selectText.1=Mode manuel de 1 à 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Niveau d'optimisation compress.selectText.2=Niveau d'optimisation

View File

@@ -951,6 +951,7 @@ fileToPDF.submit=Tiontaigh go PDF
compress.title=Comhbhrúigh compress.title=Comhbhrúigh
compress.header=Comhbhrúigh PDF compress.header=Comhbhrúigh PDF
compress.credit=Úsáideann an tseirbhís seo qpdf le haghaidh Comhbhrú/Optimization PDF. compress.credit=Úsáideann an tseirbhís seo qpdf le haghaidh Comhbhrú/Optimization PDF.
compress.grayscale.label=Cuir Scála Liath i bhFeidhm le Comhbhrú
compress.selectText.1=Mód Láimhe - Ó 1 go 5 compress.selectText.1=Mód Láimhe - Ó 1 go 5
compress.selectText.1.1=I leibhéil optamaithe 6 go 9, chomh maith le comhbhrú ginearálta PDF, déantar réiteach íomhá a laghdú de réir scála chun méid comhaid a laghdú tuilleadh. Mar thoradh ar leibhéil níos airde tá comhbhrú íomhá níos láidre (suas le 50% den mhéid bunaidh), ag baint amach laghdú méide níos mó ach le caillteanas cáilíochta féideartha in íomhánna. compress.selectText.1.1=I leibhéil optamaithe 6 go 9, chomh maith le comhbhrú ginearálta PDF, déantar réiteach íomhá a laghdú de réir scála chun méid comhaid a laghdú tuilleadh. Mar thoradh ar leibhéil níos airde tá comhbhrú íomhá níos láidre (suas le 50% den mhéid bunaidh), ag baint amach laghdú méide níos mó ach le caillteanas cáilíochta féideartha in íomhánna.
compress.selectText.2=Leibhéal optamaithe: compress.selectText.2=Leibhéal optamaithe:

View File

@@ -572,8 +572,8 @@ login.invalid=अमान्य उपयोगकर्ता नाम या
login.locked=आपका खाता लॉक कर दिया गया है। login.locked=आपका खाता लॉक कर दिया गया है।
login.signinTitle=कृपया साइन इन करें login.signinTitle=कृपया साइन इन करें
login.ssoSignIn=सिंगल साइन-ऑन के माध्यम से लॉगिन करें login.ssoSignIn=सिंगल साइन-ऑन के माध्यम से लॉगिन करें
login.oauth2AutoCreateDisabled=OAUTH2 स्वतः उपयोगकर्ता निर्माण अक्षम है login.oAuth2AutoCreateDisabled=OAUTH2 स्वतः उपयोगकर्ता निर्माण अक्षम है
login.oauth2AdminBlockedUser=गैर-पंजीकृत उपयोगकर्ताओं का पंजीकरण या लॉगिन वर्तमान में अवरुद्ध है। कृपया व्यवस्थापक से संपर्क करें। login.oAuth2AdminBlockedUser=गैर-पंजीकृत उपयोगकर्ताओं का पंजीकरण या लॉगिन वर्तमान में अवरुद्ध है। कृपया व्यवस्थापक से संपर्क करें।
login.oauth2RequestNotFound=प्राधिकरण अनुरोध नहीं मिला login.oauth2RequestNotFound=प्राधिकरण अनुरोध नहीं मिला
login.oauth2InvalidUserInfoResponse=अमान्य उपयोगकर्ता जानकारी प्रतिक्रिया login.oauth2InvalidUserInfoResponse=अमान्य उपयोगकर्ता जानकारी प्रतिक्रिया
login.oauth2invalidRequest=अमान्य अनुरोध login.oauth2invalidRequest=अमान्य अनुरोध
@@ -951,6 +951,7 @@ fileToPDF.submit=PDF में बदलें
compress.title=कम्प्रेस compress.title=कम्प्रेस
compress.header=PDF कम्प्रेस करें compress.header=PDF कम्प्रेस करें
compress.credit=यह सेवा PDF कम्प्रेस/अनुकूलन के लिए qpdf का उपयोग करती है। compress.credit=यह सेवा PDF कम्प्रेस/अनुकूलन के लिए qpdf का उपयोग करती है।
compress.grayscale.label=संपीड़न के लिए ग्रेस्केल लागू करें
compress.selectText.1=मैनुअल मोड - स्तर 1 से 4 compress.selectText.1=मैनुअल मोड - स्तर 1 से 4
compress.selectText.1.1=अनुकूलन स्तर 6 से 9 में, सामान्य PDF कम्प्रेसन के अतिरिक्त, फ़ाइल आकार को और कम करने के लिए छवि रेज़ोल्यूशन को कम किया जाता है। उच्च स्तर पर छवियों का अधिक कम्प्रेसन होता है (मूल आकार का 50% तक), जिससे आकार में अधिक कमी आती है लेकिन छवियों की गुणवत्ता प्रभावित हो सकती है। compress.selectText.1.1=अनुकूलन स्तर 6 से 9 में, सामान्य PDF कम्प्रेसन के अतिरिक्त, फ़ाइल आकार को और कम करने के लिए छवि रेज़ोल्यूशन को कम किया जाता है। उच्च स्तर पर छवियों का अधिक कम्प्रेसन होता है (मूल आकार का 50% तक), जिससे आकार में अधिक कमी आती है लेकिन छवियों की गुणवत्ता प्रभावित हो सकती है।
compress.selectText.2=अनुकूलन स्तर: compress.selectText.2=अनुकूलन स्तर:

View File

@@ -572,8 +572,8 @@ login.invalid=Neispravno korisničko ime ili zaporka.
login.locked=Vaš račun je zaključan. login.locked=Vaš račun je zaključan.
login.signinTitle=Molimo vas da se prijavite login.signinTitle=Molimo vas da se prijavite
login.ssoSignIn=Prijavite se putem jedinstvene prijave login.ssoSignIn=Prijavite se putem jedinstvene prijave
login.oauth2AutoCreateDisabled=OAUTH2 automatsko kreiranje korisnika je onemogućeno login.oAuth2AutoCreateDisabled=OAUTH2 automatsko kreiranje korisnika je onemogućeno
login.oauth2AdminBlockedUser=Registracija ili prijava nekadreguiranih korisnika trenutno su blokirane. Molimo Vas da kontaktirate administratora. login.oAuth2AdminBlockedUser=Registracija ili prijava nekadreguiranih korisnika trenutno su blokirane. Molimo Vas da kontaktirate administratora.
login.oauth2RequestNotFound=Zahtjev za autorizaciju nije pronađen login.oauth2RequestNotFound=Zahtjev za autorizaciju nije pronađen
login.oauth2InvalidUserInfoResponse=Nevažeće informacije o korisniku login.oauth2InvalidUserInfoResponse=Nevažeće informacije o korisniku
login.oauth2invalidRequest=Neispravan zahtjev login.oauth2invalidRequest=Neispravan zahtjev
@@ -951,6 +951,7 @@ fileToPDF.submit=Pretvori u PDF
compress.title=Komprimirajte compress.title=Komprimirajte
compress.header=Komprimirajte PDF compress.header=Komprimirajte PDF
compress.credit=Ova usluga koristi qpdf za komprimiranje / optimizaciju PDF-a. compress.credit=Ova usluga koristi qpdf za komprimiranje / optimizaciju PDF-a.
compress.grayscale.label=Primijeni sivinu za kompresiju
compress.selectText.1=Ručni režim - Od 1 do 5 compress.selectText.1=Ručni režim - Od 1 do 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Nivo optimizacije: compress.selectText.2=Nivo optimizacije:

View File

@@ -572,8 +572,8 @@ login.invalid=Érvénytelen felhasználónév vagy jelszó.
login.locked=A fiókja zárolva van. login.locked=A fiókja zárolva van.
login.signinTitle=Kérjük, jelentkezzen be login.signinTitle=Kérjük, jelentkezzen be
login.ssoSignIn=Bejelentkezés egyszeri bejelentkezéssel login.ssoSignIn=Bejelentkezés egyszeri bejelentkezéssel
login.oauth2AutoCreateDisabled=OAuth2 automatikus felhasználólétrehozás letiltva login.oAuth2AutoCreateDisabled=OAuth2 automatikus felhasználólétrehozás letiltva
login.oauth2AdminBlockedUser=A nem regisztrált felhasználók regisztrációja vagy bejelentkezése jelenleg le van tiltva. Kérjük, forduljon a rendszergazdához. login.oAuth2AdminBlockedUser=A nem regisztrált felhasználók regisztrációja vagy bejelentkezése jelenleg le van tiltva. Kérjük, forduljon a rendszergazdához.
login.oauth2RequestNotFound=A hitelesítési kérés nem található login.oauth2RequestNotFound=A hitelesítési kérés nem található
login.oauth2InvalidUserInfoResponse=Érvénytelen felhasználói információ válasz login.oauth2InvalidUserInfoResponse=Érvénytelen felhasználói információ válasz
login.oauth2invalidRequest=Érvénytelen kérés login.oauth2invalidRequest=Érvénytelen kérés
@@ -951,6 +951,7 @@ fileToPDF.submit=Konvertálás PDF-be
compress.title=Tömörítés compress.title=Tömörítés
compress.header=PDF tömörítése compress.header=PDF tömörítése
compress.credit=Ez a szolgáltatás a qpdf használatával végzi a PDF tömörítését/optimalizálását. compress.credit=Ez a szolgáltatás a qpdf használatával végzi a PDF tömörítését/optimalizálását.
compress.grayscale.label=Szürkeárnyalatok alkalmazása tömörítéshez
compress.selectText.1=Kézi mód - 1-től 5-ig compress.selectText.1=Kézi mód - 1-től 5-ig
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimalizálási szint: compress.selectText.2=Optimalizálási szint:

View File

@@ -572,8 +572,8 @@ login.invalid=Nama pengguna atau kata sandi tidak valid.
login.locked=Akun Anda telah dikunci. login.locked=Akun Anda telah dikunci.
login.signinTitle=Silakan masuk login.signinTitle=Silakan masuk
login.ssoSignIn=Masuk melalui Single Sign - on login.ssoSignIn=Masuk melalui Single Sign - on
login.oauth2AutoCreateDisabled=OAUTH2 Buat Otomatis Pengguna Dinonaktifkan login.oAuth2AutoCreateDisabled=OAUTH2 Buat Otomatis Pengguna Dinonaktifkan
login.oauth2AdminBlockedUser=Registrasi atau login pengguna yang tidak terdaftar saat ini diblokir. Silakan hubungi administrator. login.oAuth2AdminBlockedUser=Registrasi atau login pengguna yang tidak terdaftar saat ini diblokir. Silakan hubungi administrator.
login.oauth2RequestNotFound=Permintaan otorisasi tidak ditemukan login.oauth2RequestNotFound=Permintaan otorisasi tidak ditemukan
login.oauth2InvalidUserInfoResponse=Respons Info Pengguna Tidak Valid login.oauth2InvalidUserInfoResponse=Respons Info Pengguna Tidak Valid
login.oauth2invalidRequest=Permintaan Tidak Valid login.oauth2invalidRequest=Permintaan Tidak Valid
@@ -951,6 +951,7 @@ fileToPDF.submit=Konversi ke PDF
compress.title=Kompres compress.title=Kompres
compress.header=Kompres PDF compress.header=Kompres PDF
compress.credit=Layanan ini menggunakan qpdf untuk Kompresi/Optimalisasi PDF. compress.credit=Layanan ini menggunakan qpdf untuk Kompresi/Optimalisasi PDF.
compress.grayscale.label=Terapkan Skala Abu-Abu untuk Kompresi
compress.selectText.1=Mode Manual - Dari 1 hingga 5 compress.selectText.1=Mode Manual - Dari 1 hingga 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Tingkat Optimalisasi: compress.selectText.2=Tingkat Optimalisasi:

View File

@@ -572,8 +572,8 @@ login.invalid=Nome utente o password errati.
login.locked=Il tuo account è stato bloccato. login.locked=Il tuo account è stato bloccato.
login.signinTitle=Per favore accedi login.signinTitle=Per favore accedi
login.ssoSignIn=Accedi tramite Single Sign-on login.ssoSignIn=Accedi tramite Single Sign-on
login.oauth2AutoCreateDisabled=Creazione automatica utente OAUTH2 DISABILITATA login.oAuth2AutoCreateDisabled=Creazione automatica utente OAUTH2 DISABILITATA
login.oauth2AdminBlockedUser=La registrazione o l'accesso degli utenti non registrati è attualmente bloccata. Si prega di contattare l'amministratore. login.oAuth2AdminBlockedUser=La registrazione o l'accesso degli utenti non registrati è attualmente bloccata. Si prega di contattare l'amministratore.
login.oauth2RequestNotFound=Richiesta di autorizzazione non trovata login.oauth2RequestNotFound=Richiesta di autorizzazione non trovata
login.oauth2InvalidUserInfoResponse=Risposta relativa alle informazioni utente non valida login.oauth2InvalidUserInfoResponse=Risposta relativa alle informazioni utente non valida
login.oauth2invalidRequest=Richiesta non valida login.oauth2invalidRequest=Richiesta non valida
@@ -951,6 +951,7 @@ fileToPDF.submit=Converti in PDF
compress.title=Comprimi compress.title=Comprimi
compress.header=Comprimi PDF compress.header=Comprimi PDF
compress.credit=Questo servizio utilizza qpdf per la compressione/ottimizzazione dei PDF. compress.credit=Questo servizio utilizza qpdf per la compressione/ottimizzazione dei PDF.
compress.grayscale.label=Applica scala di grigio per la compressione
compress.selectText.1=Modalità manuale - Da 1 a 5 compress.selectText.1=Modalità manuale - Da 1 a 5
compress.selectText.1.1=Nei livelli di ottimizzazione da 6 a 9, oltre alla compressione PDF generale, la risoluzione dell'immagine viene ridotta per ridurre ulteriormente le dimensioni del file. Livelli più alti comportano una compressione dell'immagine più forte (fino al 50% delle dimensioni originali), ottenendo una maggiore riduzione delle dimensioni ma con una potenziale perdita di qualità nelle immagini. compress.selectText.1.1=Nei livelli di ottimizzazione da 6 a 9, oltre alla compressione PDF generale, la risoluzione dell'immagine viene ridotta per ridurre ulteriormente le dimensioni del file. Livelli più alti comportano una compressione dell'immagine più forte (fino al 50% delle dimensioni originali), ottenendo una maggiore riduzione delle dimensioni ma con una potenziale perdita di qualità nelle immagini.
compress.selectText.2=Livello di ottimizzazione: compress.selectText.2=Livello di ottimizzazione:

View File

@@ -572,8 +572,8 @@ login.invalid=ユーザー名かパスワードが無効です。
login.locked=あなたのアカウントはロックされています。 login.locked=あなたのアカウントはロックされています。
login.signinTitle=サインインしてください login.signinTitle=サインインしてください
login.ssoSignIn=シングルサインオンでログイン login.ssoSignIn=シングルサインオンでログイン
login.oauth2AutoCreateDisabled=OAuth 2自動作成ユーザーが無効 login.oAuth2AutoCreateDisabled=OAuth 2自動作成ユーザーが無効
login.oauth2AdminBlockedUser=現在、未登録ユーザーの登録またはログインはブロックされています。管理者にお問い合わせください。 login.oAuth2AdminBlockedUser=現在、未登録ユーザーの登録またはログインはブロックされています。管理者にお問い合わせください。
login.oauth2RequestNotFound=認証リクエストが見つかりません login.oauth2RequestNotFound=認証リクエストが見つかりません
login.oauth2InvalidUserInfoResponse=無効なユーザー情報の応答 login.oauth2InvalidUserInfoResponse=無効なユーザー情報の応答
login.oauth2invalidRequest=無効なリクエスト login.oauth2invalidRequest=無効なリクエスト
@@ -951,6 +951,7 @@ fileToPDF.submit=PDFを変換
compress.title=圧縮 compress.title=圧縮
compress.header=PDFを圧縮 compress.header=PDFを圧縮
compress.credit=本サービスはPDFの圧縮/最適化にqpdfを使用しています。 compress.credit=本サービスはPDFの圧縮/最適化にqpdfを使用しています。
compress.grayscale.label=圧縮にグレースケールを適用する
compress.selectText.1=手動モード - 1から9 compress.selectText.1=手動モード - 1から9
compress.selectText.1.1=最適化レベル69では、一般的なPDF圧縮に加えて画像解像度が縮小され、ファイルサイズがさらに縮小されます。レベルが高くなると、画像圧縮が強化され (元のサイズの最大 50%)、サイズはさらに縮小されますが、画像の品質が低下する可能性があります。 compress.selectText.1.1=最適化レベル69では、一般的なPDF圧縮に加えて画像解像度が縮小され、ファイルサイズがさらに縮小されます。レベルが高くなると、画像圧縮が強化され (元のサイズの最大 50%)、サイズはさらに縮小されますが、画像の品質が低下する可能性があります。
compress.selectText.2=品質レベル: compress.selectText.2=品質レベル:

View File

@@ -572,8 +572,8 @@ login.invalid=사용자 이름 또는 비밀번호가 잘못되었습니다.
login.locked=계정이 잠겼습니다. login.locked=계정이 잠겼습니다.
login.signinTitle=로그인해 주세요 login.signinTitle=로그인해 주세요
login.ssoSignIn=단일 로그인으로 로그인 login.ssoSignIn=단일 로그인으로 로그인
login.oauth2AutoCreateDisabled=OAuth2 사용자 자동 생성이 비활성화되었습니다 login.oAuth2AutoCreateDisabled=OAuth2 사용자 자동 생성이 비활성화되었습니다
login.oauth2AdminBlockedUser=현재 미등록 사용자의 등록 또는 로그인이 차단되어 있습니다. 관리자에게 문의하세요. login.oAuth2AdminBlockedUser=현재 미등록 사용자의 등록 또는 로그인이 차단되어 있습니다. 관리자에게 문의하세요.
login.oauth2RequestNotFound=인증 요청을 찾을 수 없습니다 login.oauth2RequestNotFound=인증 요청을 찾을 수 없습니다
login.oauth2InvalidUserInfoResponse=잘못된 사용자 정보 응답 login.oauth2InvalidUserInfoResponse=잘못된 사용자 정보 응답
login.oauth2invalidRequest=잘못된 요청 login.oauth2invalidRequest=잘못된 요청
@@ -951,6 +951,7 @@ fileToPDF.submit=PDF로 변환
compress.title=압축 compress.title=압축
compress.header=PDF 압축 compress.header=PDF 압축
compress.credit=이 서비스는 PDF 압축/최적화를 위해 qpdf를 사용합니다. compress.credit=이 서비스는 PDF 압축/최적화를 위해 qpdf를 사용합니다.
compress.grayscale.label=압축을 위해 그레이스케일 적용
compress.selectText.1=수동 모드 - 1에서 5 compress.selectText.1=수동 모드 - 1에서 5
compress.selectText.1.1=최적화 레벨 6에서 9에서는 일반적인 PDF 압축 외에도 이미지 해상도가 낮아져 파일 크기가 더욱 감소합니다. 높은 레벨은 더 강력한 이미지 압축(원본 크기의 최대 50%)을 초래하여 더 큰 크기 감소를 달성하지만 이미지 품질이 저하될 수 있습니다. compress.selectText.1.1=최적화 레벨 6에서 9에서는 일반적인 PDF 압축 외에도 이미지 해상도가 낮아져 파일 크기가 더욱 감소합니다. 높은 레벨은 더 강력한 이미지 압축(원본 크기의 최대 50%)을 초래하여 더 큰 크기 감소를 달성하지만 이미지 품질이 저하될 수 있습니다.
compress.selectText.2=최적화 레벨: compress.selectText.2=최적화 레벨:

View File

@@ -572,8 +572,8 @@ login.invalid=Ongeldige gebruikersnaam of wachtwoord.
login.locked=Je account is geblokkeerd. login.locked=Je account is geblokkeerd.
login.signinTitle=Gelieve in te loggen login.signinTitle=Gelieve in te loggen
login.ssoSignIn=Inloggen via Single Sign-on login.ssoSignIn=Inloggen via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Automatisch aanmaken gebruiker uitgeschakeld login.oAuth2AutoCreateDisabled=OAUTH2 Automatisch aanmaken gebruiker uitgeschakeld
login.oauth2AdminBlockedUser=Registratie of inloggen van niet-registreerde gebruikers is helaas momenteel geblokkeerd. Neem contact op met de beheerder. login.oAuth2AdminBlockedUser=Registratie of inloggen van niet-registreerde gebruikers is helaas momenteel geblokkeerd. Neem contact op met de beheerder.
login.oauth2RequestNotFound=Autorisatieverzoek niet gevonden login.oauth2RequestNotFound=Autorisatieverzoek niet gevonden
login.oauth2InvalidUserInfoResponse=Ongeldige reactie op gebruikersinfo login.oauth2InvalidUserInfoResponse=Ongeldige reactie op gebruikersinfo
login.oauth2invalidRequest=Ongeldig verzoek login.oauth2invalidRequest=Ongeldig verzoek
@@ -951,6 +951,7 @@ fileToPDF.submit=Omzetten naar PDF
compress.title=Comprimeren compress.title=Comprimeren
compress.header=PDF comprimeren compress.header=PDF comprimeren
compress.credit=Deze functie gebruikt qpdf voor PDF Compressie/Optimalisatie. compress.credit=Deze functie gebruikt qpdf voor PDF Compressie/Optimalisatie.
compress.grayscale.label=Grijsschaal toepassen voor compressie
compress.selectText.1=Handmatige modus - Van 1 tot 5 compress.selectText.1=Handmatige modus - Van 1 tot 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimalisatieniveau: compress.selectText.2=Optimalisatieniveau:

View File

@@ -572,8 +572,8 @@ login.invalid=Ugyldig brukernavn eller passord.
login.locked=Kontoen din har blitt låst. login.locked=Kontoen din har blitt låst.
login.signinTitle=Vennligst logg inn login.signinTitle=Vennligst logg inn
login.ssoSignIn=Logg inn via Enkel Pålogging login.ssoSignIn=Logg inn via Enkel Pålogging
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Opretting av bruker deaktivert login.oAuth2AutoCreateDisabled=OAUTH2 Auto-Opretting av bruker deaktivert
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator. login.oAuth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Autentiseringsforespørsel ikke funnet login.oauth2RequestNotFound=Autentiseringsforespørsel ikke funnet
login.oauth2InvalidUserInfoResponse=Ugyldig brukerinforespons login.oauth2InvalidUserInfoResponse=Ugyldig brukerinforespons
login.oauth2invalidRequest=Ugyldig forespørsel login.oauth2invalidRequest=Ugyldig forespørsel
@@ -951,6 +951,7 @@ fileToPDF.submit=Konverter til PDF
compress.title=Komprimer compress.title=Komprimer
compress.header=Komprimer PDF compress.header=Komprimer PDF
compress.credit=Denne tjenesten bruker qpdf for PDF-komprimering/optimisering. compress.credit=Denne tjenesten bruker qpdf for PDF-komprimering/optimisering.
compress.grayscale.label=Bruk gråskala for komprimering
compress.selectText.1=Manuell modus - Fra 1 til 5 compress.selectText.1=Manuell modus - Fra 1 til 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimeringsnivå: compress.selectText.2=Optimeringsnivå:

View File

@@ -572,8 +572,8 @@ login.invalid=Nieprawidłowe dane logowania
login.locked=Konto jest zablokowane login.locked=Konto jest zablokowane
login.signinTitle=Zaloguj się login.signinTitle=Zaloguj się
login.ssoSignIn=Zaloguj się za pomocą logowania jednokrotnego login.ssoSignIn=Zaloguj się za pomocą logowania jednokrotnego
login.oauth2AutoCreateDisabled=Wyłączono automatyczne tworzenie użytkownika OAUTH2 login.oAuth2AutoCreateDisabled=Wyłączono automatyczne tworzenie użytkownika OAUTH2
login.oauth2AdminBlockedUser=Rejestracja lub logowanie niezarejestrowanych użytkowników jest obecnie zablokowane. Prosimy o kontakt z administratorem. login.oAuth2AdminBlockedUser=Rejestracja lub logowanie niezarejestrowanych użytkowników jest obecnie zablokowane. Prosimy o kontakt z administratorem.
login.oauth2RequestNotFound=Błąd logowania OAuth2 login.oauth2RequestNotFound=Błąd logowania OAuth2
login.oauth2InvalidUserInfoResponse=Niewłaściwe dane logowania login.oauth2InvalidUserInfoResponse=Niewłaściwe dane logowania
login.oauth2invalidRequest=Nieprawidłowe żądanie login.oauth2invalidRequest=Nieprawidłowe żądanie
@@ -951,6 +951,7 @@ fileToPDF.submit=Konwertuj na PDF
compress.title=Kompresuj compress.title=Kompresuj
compress.header=Kompresuj PDF compress.header=Kompresuj PDF
compress.credit=Ta usługa używa qpdf do kompresji/optymalizacji PDF. compress.credit=Ta usługa używa qpdf do kompresji/optymalizacji PDF.
compress.grayscale.label=Zastosuj skalę szarości do kompresji
compress.selectText.1=Tryb ręczny - Od 1 do 5 compress.selectText.1=Tryb ręczny - Od 1 do 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Poziom optymalizacji: compress.selectText.2=Poziom optymalizacji:

View File

@@ -572,8 +572,8 @@ login.invalid=Usuário ou senha inválidos.
login.locked=Sua conta foi bloqueada. login.locked=Sua conta foi bloqueada.
login.signinTitle=Por favor, inicie a sessão login.signinTitle=Por favor, inicie a sessão
login.ssoSignIn=Iniciar sessão através de login único (SSO) login.ssoSignIn=Iniciar sessão através de login único (SSO)
login.oauth2AutoCreateDisabled=Auto-Criar Usuário OAUTH2 Desativado login.oAuth2AutoCreateDisabled=Auto-Criar Usuário OAUTH2 Desativado
login.oauth2AdminBlockedUser=O registro ou login de usuários não registrados está atualmente bloqueado. Entre em contato com o administrador. login.oAuth2AdminBlockedUser=O registro ou login de usuários não registrados está atualmente bloqueado. Entre em contato com o administrador.
login.oauth2RequestNotFound=Solicitação de autorização não encontrada login.oauth2RequestNotFound=Solicitação de autorização não encontrada
login.oauth2InvalidUserInfoResponse=Resposta de informação de usuário inválida login.oauth2InvalidUserInfoResponse=Resposta de informação de usuário inválida
login.oauth2invalidRequest=Requisição Inválida login.oauth2invalidRequest=Requisição Inválida
@@ -951,6 +951,7 @@ fileToPDF.submit=Converter para PDF
compress.title=Comprimir compress.title=Comprimir
compress.header=Comprimir compress.header=Comprimir
compress.credit=Este serviço usa o Qpdf para compressão/otimização de PDF. compress.credit=Este serviço usa o Qpdf para compressão/otimização de PDF.
compress.grayscale.label=Aplicar escala de cinza para compressão
compress.selectText.1=Modo Manual - De 1 a 9 compress.selectText.1=Modo Manual - De 1 a 9
compress.selectText.1.1=Nos níveis de otimização 6-9, além da compressão normal do PDF, a resolução das imagens são reduzidas, para diminuir ainda mais o tamanho do arquivo. Quanto maior o nível, maior a compressão da imagem (até 50% do tamanho original), resultando em tamanho menor do arquivo, porém com menor qualidade nas imagens. compress.selectText.1.1=Nos níveis de otimização 6-9, além da compressão normal do PDF, a resolução das imagens são reduzidas, para diminuir ainda mais o tamanho do arquivo. Quanto maior o nível, maior a compressão da imagem (até 50% do tamanho original), resultando em tamanho menor do arquivo, porém com menor qualidade nas imagens.
compress.selectText.2=Nível de Otimização: compress.selectText.2=Nível de Otimização:

View File

@@ -572,8 +572,8 @@ login.invalid=Nome de utilizador ou palavra-passe inválidos.
login.locked=A sua conta foi bloqueada. login.locked=A sua conta foi bloqueada.
login.signinTitle=Por favor inicie sessão login.signinTitle=Por favor inicie sessão
login.ssoSignIn=Login via Single Sign-on login.ssoSignIn=Login via Single Sign-on
login.oauth2AutoCreateDisabled=Criação Automática de Utilizador OAUTH2 Desativada login.oAuth2AutoCreateDisabled=Criação Automática de Utilizador OAUTH2 Desativada
login.oauth2AdminBlockedUser=O registo ou login de utilizadores não registados está atualmente bloqueado. Por favor contacte o administrador. login.oAuth2AdminBlockedUser=O registo ou login de utilizadores não registados está atualmente bloqueado. Por favor contacte o administrador.
login.oauth2RequestNotFound=Pedido de autorização não encontrado login.oauth2RequestNotFound=Pedido de autorização não encontrado
login.oauth2InvalidUserInfoResponse=Resposta de Informação de Utilizador Inválida login.oauth2InvalidUserInfoResponse=Resposta de Informação de Utilizador Inválida
login.oauth2invalidRequest=Pedido Inválido login.oauth2invalidRequest=Pedido Inválido
@@ -951,6 +951,7 @@ fileToPDF.submit=Converter para PDF
compress.title=Comprimir compress.title=Comprimir
compress.header=Comprimir PDF compress.header=Comprimir PDF
compress.credit=Este serviço usa qpdf para Compressão/Otimização de PDF. compress.credit=Este serviço usa qpdf para Compressão/Otimização de PDF.
compress.grayscale.label=Aplicar escala de cinzentos para compressão
compress.selectText.1=Modo Manual - De 1 a 5 compress.selectText.1=Modo Manual - De 1 a 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Nível de otimização: compress.selectText.2=Nível de otimização:

View File

@@ -572,8 +572,8 @@ login.invalid=Nume de utilizator sau parolă invalidă.
login.locked=Contul tău a fost blocat. login.locked=Contul tău a fost blocat.
login.signinTitle=Te rugăm să te autentifici login.signinTitle=Te rugăm să te autentifici
login.ssoSignIn=Conectare prin conectare unică login.ssoSignIn=Conectare prin conectare unică
login.oauth2AutoCreateDisabled=OAUTH2 Creare automată utilizator dezactivată login.oAuth2AutoCreateDisabled=OAUTH2 Creare automată utilizator dezactivată
login.oauth2AdminBlockedUser=Înregistrarea sau conectarea utilizatorilor neînregistrați este în prezent blocată. Te rugăm să contactezi administratorul. login.oAuth2AdminBlockedUser=Înregistrarea sau conectarea utilizatorilor neînregistrați este în prezent blocată. Te rugăm să contactezi administratorul.
login.oauth2RequestNotFound=Cererea de autorizare nu a fost găsită login.oauth2RequestNotFound=Cererea de autorizare nu a fost găsită
login.oauth2InvalidUserInfoResponse=Răspuns Invalid la Informațiile Utilizatorului login.oauth2InvalidUserInfoResponse=Răspuns Invalid la Informațiile Utilizatorului
login.oauth2invalidRequest=Cerere Invalidă login.oauth2invalidRequest=Cerere Invalidă
@@ -951,6 +951,7 @@ fileToPDF.submit=Convertiți în PDF
compress.title=Comprimare compress.title=Comprimare
compress.header=Comprimare PDF compress.header=Comprimare PDF
compress.credit=Acest serviciu utilizează qpdf pentru comprimarea/optimizarea PDF-urilor. compress.credit=Acest serviciu utilizează qpdf pentru comprimarea/optimizarea PDF-urilor.
compress.grayscale.label=Aplicare scală de gri pentru compresie
compress.selectText.1=Modul manual - de la 1 la 5 compress.selectText.1=Modul manual - de la 1 la 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Nivel de optimizare: compress.selectText.2=Nivel de optimizare:

View File

@@ -572,8 +572,8 @@ login.invalid=Неверное имя пользователя или парол
login.locked=Ваша учетная запись заблокирована. login.locked=Ваша учетная запись заблокирована.
login.signinTitle=Пожалуйста, войдите login.signinTitle=Пожалуйста, войдите
login.ssoSignIn=Вход через единый вход login.ssoSignIn=Вход через единый вход
login.oauth2AutoCreateDisabled=Автоматическое создание пользователей OAuth2 отключено login.oAuth2AutoCreateDisabled=Автоматическое создание пользователей OAuth2 отключено
login.oauth2AdminBlockedUser=Регистрация или вход незарегистрированных пользователей в настоящее время заблокированы. Обратитесь к администратору. login.oAuth2AdminBlockedUser=Регистрация или вход незарегистрированных пользователей в настоящее время заблокированы. Обратитесь к администратору.
login.oauth2RequestNotFound=Запрос авторизации не найден login.oauth2RequestNotFound=Запрос авторизации не найден
login.oauth2InvalidUserInfoResponse=Недействительный ответ с информацией о пользователе login.oauth2InvalidUserInfoResponse=Недействительный ответ с информацией о пользователе
login.oauth2invalidRequest=Недействительный запрос login.oauth2invalidRequest=Недействительный запрос
@@ -951,6 +951,7 @@ fileToPDF.submit=Преобразовать в PDF
compress.title=Сжать compress.title=Сжать
compress.header=Сжать PDF compress.header=Сжать PDF
compress.credit=Этот сервис использует qpdf для сжатия/оптимизации PDF. compress.credit=Этот сервис использует qpdf для сжатия/оптимизации PDF.
compress.grayscale.label=Применить шкалу серого для сжатия
compress.selectText.1=Ручной режим - от 1 до 5 compress.selectText.1=Ручной режим - от 1 до 5
compress.selectText.1.1=На уровнях оптимизации от 6 до 9, помимо общего сжатия PDF, разрешение изображений уменьшается для дальнейшего сокращения размера файла. Более высокие уровни приводят к более сильному сжатию изображений (до 50% от исходного размера), обеспечивая большее уменьшение размера, но с возможной потерей качества изображений. compress.selectText.1.1=На уровнях оптимизации от 6 до 9, помимо общего сжатия PDF, разрешение изображений уменьшается для дальнейшего сокращения размера файла. Более высокие уровни приводят к более сильному сжатию изображений (до 50% от исходного размера), обеспечивая большее уменьшение размера, но с возможной потерей качества изображений.
compress.selectText.2=Уровень оптимизации: compress.selectText.2=Уровень оптимизации:

View File

@@ -572,8 +572,8 @@ login.invalid=Neplatné používateľské meno alebo heslo.
login.locked=Váš účet bol uzamknutý. login.locked=Váš účet bol uzamknutý.
login.signinTitle=Prosím, prihláste sa login.signinTitle=Prosím, prihláste sa
login.ssoSignIn=Prihlásiť sa cez Single Sign-on login.ssoSignIn=Prihlásiť sa cez Single Sign-on
login.oauth2AutoCreateDisabled=Vytváranie používateľa cez OAUTH2 je zakázané login.oAuth2AutoCreateDisabled=Vytváranie používateľa cez OAUTH2 je zakázané
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator. login.oAuth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
@@ -951,6 +951,7 @@ fileToPDF.submit=Konvertovať do PDF
compress.title=Komprimovať compress.title=Komprimovať
compress.header=Komprimovať PDF compress.header=Komprimovať PDF
compress.credit=Táto služba používa qpdf pre kompresiu/optimalizáciu PDF. compress.credit=Táto služba používa qpdf pre kompresiu/optimalizáciu PDF.
compress.grayscale.label=Použiť odtiene šedej na kompresiu
compress.selectText.1=Manuálny režim - Od 1 do 5 compress.selectText.1=Manuálny režim - Od 1 do 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Úroveň optimalizácie: compress.selectText.2=Úroveň optimalizácie:

View File

@@ -572,8 +572,8 @@ login.invalid=Neveljavno uporabniško ime ali geslo.
login.locked=Vaš račun je bil zaklenjen. login.locked=Vaš račun je bil zaklenjen.
login.signinTitle=Prosim prijavite se login.signinTitle=Prosim prijavite se
login.ssoSignIn=Prijava prek enotne prijave login.ssoSignIn=Prijava prek enotne prijave
login.oauth2AutoCreateDisabled=OAUTH2 Samodejno ustvarjanje uporabnika onemogočeno login.oAuth2AutoCreateDisabled=OAUTH2 Samodejno ustvarjanje uporabnika onemogočeno
login.oauth2AdminBlockedUser=Registracija ali prijava neregistriranih uporabnikov je trenutno blokirana. Prosimo kontaktirajte skrbnika. login.oAuth2AdminBlockedUser=Registracija ali prijava neregistriranih uporabnikov je trenutno blokirana. Prosimo kontaktirajte skrbnika.
login.oauth2RequestNotFound=Zahteva za avtorizacijo ni bila najdena login.oauth2RequestNotFound=Zahteva za avtorizacijo ni bila najdena
login.oauth2InvalidUserInfoResponse=Neveljaven odgovor z informacijami o uporabniku login.oauth2InvalidUserInfoResponse=Neveljaven odgovor z informacijami o uporabniku
login.oauth2invalidRequest=Neveljavna zahteva login.oauth2invalidRequest=Neveljavna zahteva
@@ -951,6 +951,7 @@ fileToPDF.submit=Pretvori v PDF
compress.title=Stisnite compress.title=Stisnite
compress.header=Stisnite PDF compress.header=Stisnite PDF
compress.credit=Ta storitev uporablja qpdf za stiskanje/optimizacijo PDF. compress.credit=Ta storitev uporablja qpdf za stiskanje/optimizacijo PDF.
compress.grayscale.label=Uporabi sivinsko lestvico za stiskanje
compress.selectText.1=Ročni način - Od 1 do 5 compress.selectText.1=Ročni način - Od 1 do 5
compress.selectText.1.1=Na stopnjah optimizacije od 6 do 9 je poleg splošnega stiskanja PDF ločljivost slike zmanjšana, da se dodatno zmanjša velikost datoteke. Višje ravni povzročijo močnejše stiskanje slike (do 50 % prvotne velikosti), s čimer se doseže večje zmanjšanje velikosti, vendar s potencialno izgubo kakovosti slik. compress.selectText.1.1=Na stopnjah optimizacije od 6 do 9 je poleg splošnega stiskanja PDF ločljivost slike zmanjšana, da se dodatno zmanjša velikost datoteke. Višje ravni povzročijo močnejše stiskanje slike (do 50 % prvotne velikosti), s čimer se doseže večje zmanjšanje velikosti, vendar s potencialno izgubo kakovosti slik.
compress.selectText.2=Raven optimizacije: compress.selectText.2=Raven optimizacije:

View File

@@ -572,8 +572,8 @@ login.invalid=Neispravno korisničko ime ili lozinka.
login.locked=Vaš nalog je zaključan. login.locked=Vaš nalog je zaključan.
login.signinTitle=Molimo vas da se prijavite login.signinTitle=Molimo vas da se prijavite
login.ssoSignIn=Prijavite se putem jedinstvene prijave login.ssoSignIn=Prijavite se putem jedinstvene prijave
login.oauth2AutoCreateDisabled=OAUTH2 automatsko kreiranje korisnika je onemogućeno login.oAuth2AutoCreateDisabled=OAUTH2 automatsko kreiranje korisnika je onemogućeno
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator. login.oAuth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
@@ -951,6 +951,7 @@ fileToPDF.submit=Konvertuj u PDF
compress.title=Kompresija compress.title=Kompresija
compress.header=Kompresuj PDF compress.header=Kompresuj PDF
compress.credit=Ova usluga koristi qpdf za kompresiju / optimizaciju PDF-a. compress.credit=Ova usluga koristi qpdf za kompresiju / optimizaciju PDF-a.
compress.grayscale.label=Primeni sivinu za kompresiju
compress.selectText.1=Ručni režim - Od 1 do 5 compress.selectText.1=Ručni režim - Od 1 do 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Nivo optimizacije: compress.selectText.2=Nivo optimizacije:

View File

@@ -572,8 +572,8 @@ login.invalid=Ogiltigt användarnamn eller lösenord.
login.locked=Ditt konto har låsts. login.locked=Ditt konto har låsts.
login.signinTitle=Vänligen logga in login.signinTitle=Vänligen logga in
login.ssoSignIn=Logga in via enkel inloggning login.ssoSignIn=Logga in via enkel inloggning
login.oauth2AutoCreateDisabled=OAUTH2 Auto-skapa användare inaktiverad login.oAuth2AutoCreateDisabled=OAUTH2 Auto-skapa användare inaktiverad
login.oauth2AdminBlockedUser=Registrering eller inloggning av icke-registrerade användare är för närvarande blockerad. Kontakta administratören. login.oAuth2AdminBlockedUser=Registrering eller inloggning av icke-registrerade användare är för närvarande blockerad. Kontakta administratören.
login.oauth2RequestNotFound=Auktoriseringsbegäran hittades inte login.oauth2RequestNotFound=Auktoriseringsbegäran hittades inte
login.oauth2InvalidUserInfoResponse=Ogiltigt svar på användarinformation login.oauth2InvalidUserInfoResponse=Ogiltigt svar på användarinformation
login.oauth2invalidRequest=Ogiltig begäran login.oauth2invalidRequest=Ogiltig begäran
@@ -951,6 +951,7 @@ fileToPDF.submit=Konvertera till PDF
compress.title=Komprimera compress.title=Komprimera
compress.header=Komprimera PDF compress.header=Komprimera PDF
compress.credit=Denna tjänst använder qpdf för PDF-komprimering/optimering. compress.credit=Denna tjänst använder qpdf för PDF-komprimering/optimering.
compress.grayscale.label=Tillämpa gråskala för komprimering
compress.selectText.1=Manuellt läge - Från 1 till 5 compress.selectText.1=Manuellt läge - Från 1 till 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimeringsnivå: compress.selectText.2=Optimeringsnivå:

View File

@@ -572,8 +572,8 @@ login.invalid=ชื่อผู้ใช้หรือรหัสผ่าน
login.locked=บัญชีของคุณถูกล็อค login.locked=บัญชีของคุณถูกล็อค
login.signinTitle=กรุณาลงชื่อเข้าใช้ login.signinTitle=กรุณาลงชื่อเข้าใช้
login.ssoSignIn=เข้าสู่ระบบด้วย Single Sign-on login.ssoSignIn=เข้าสู่ระบบด้วย Single Sign-on
login.oauth2AutoCreateDisabled=การสร้างผู้ใช้ OAuth2 อัตโนมัติถูกปิดใช้งาน login.oAuth2AutoCreateDisabled=การสร้างผู้ใช้ OAuth2 อัตโนมัติถูกปิดใช้งาน
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator. login.oAuth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=ไม่พบคำขอการอนุญาต login.oauth2RequestNotFound=ไม่พบคำขอการอนุญาต
login.oauth2InvalidUserInfoResponse=การตอบกลับข้อมูลผู้ใช้ไม่ถูกต้อง login.oauth2InvalidUserInfoResponse=การตอบกลับข้อมูลผู้ใช้ไม่ถูกต้อง
login.oauth2invalidRequest=คำขอไม่ถูกต้อง login.oauth2invalidRequest=คำขอไม่ถูกต้อง
@@ -951,6 +951,7 @@ fileToPDF.submit=แปลงเป็น PDF
compress.title=บีบอัด compress.title=บีบอัด
compress.header=บีบอัด PDF compress.header=บีบอัด PDF
compress.credit=บริการนี้ใช้ qpdf สำหรับการบีบอัด/การเพิ่มประสิทธิภาพ PDF compress.credit=บริการนี้ใช้ qpdf สำหรับการบีบอัด/การเพิ่มประสิทธิภาพ PDF
compress.grayscale.label=ใช้ระดับสีเทาสำหรับการบีบอัด
compress.selectText.1=โหมดแมนนวล - จาก 1 ถึง 5 compress.selectText.1=โหมดแมนนวล - จาก 1 ถึง 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=ระดับการเพิ่มประสิทธิภาพ: compress.selectText.2=ระดับการเพิ่มประสิทธิภาพ:

View File

@@ -572,8 +572,8 @@ login.invalid=Geçersiz kullanıcı adı veya şifre.
login.locked=Hesabınız kilitlendi. login.locked=Hesabınız kilitlendi.
login.signinTitle=Lütfen giriş yapınız. login.signinTitle=Lütfen giriş yapınız.
login.ssoSignIn=Tek Oturum Açma ile Giriş Yap login.ssoSignIn=Tek Oturum Açma ile Giriş Yap
login.oauth2AutoCreateDisabled=OAUTH2 Otomatik Oluşturma Kullanıcı Devre Dışı Bırakıldı login.oAuth2AutoCreateDisabled=OAUTH2 Otomatik Oluşturma Kullanıcı Devre Dışı Bırakıldı
login.oauth2AdminBlockedUser=Kayıtlı olmayan kullanıcıların kayıt veya giriş yapması şu anda engellenmiştir. Lütfen yöneticiyle iletişime geçin. login.oAuth2AdminBlockedUser=Kayıtlı olmayan kullanıcıların kayıt veya giriş yapması şu anda engellenmiştir. Lütfen yöneticiyle iletişime geçin.
login.oauth2RequestNotFound=Yetkilendirme isteği bulunamadı login.oauth2RequestNotFound=Yetkilendirme isteği bulunamadı
login.oauth2InvalidUserInfoResponse=Geçersiz Kullanıcı Bilgisi Yanıtı login.oauth2InvalidUserInfoResponse=Geçersiz Kullanıcı Bilgisi Yanıtı
login.oauth2invalidRequest=Geçersiz İstek login.oauth2invalidRequest=Geçersiz İstek
@@ -951,6 +951,7 @@ fileToPDF.submit=PDF'e Dönüştür
compress.title=Sıkıştır compress.title=Sıkıştır
compress.header=PDF'i Sıkıştır compress.header=PDF'i Sıkıştır
compress.credit=Bu hizmet PDF Sıkıştırma/Optimizasyonu için qpdf kullanır. compress.credit=Bu hizmet PDF Sıkıştırma/Optimizasyonu için qpdf kullanır.
compress.grayscale.label=Sıkıştırma için Gri Ton Uygula
compress.selectText.1=Manuel Mod - 1'den 5'e compress.selectText.1=Manuel Mod - 1'den 5'e
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimizasyon seviyesi: compress.selectText.2=Optimizasyon seviyesi:

View File

@@ -572,8 +572,8 @@ login.invalid=Недійсне ім'я користувача або парол
login.locked=Ваш обліковий запис заблоковано. login.locked=Ваш обліковий запис заблоковано.
login.signinTitle=Будь ласка, увійдіть login.signinTitle=Будь ласка, увійдіть
login.ssoSignIn=Увійти через єдиний вхід login.ssoSignIn=Увійти через єдиний вхід
login.oauth2AutoCreateDisabled=Автоматичне створення користувача OAUTH2 ВИМКНЕНО login.oAuth2AutoCreateDisabled=Автоматичне створення користувача OAUTH2 ВИМКНЕНО
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator. login.oAuth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Запит на авторизація не знайдено login.oauth2RequestNotFound=Запит на авторизація не знайдено
login.oauth2InvalidUserInfoResponse=Недійсна відповідь з інформацією користувача login.oauth2InvalidUserInfoResponse=Недійсна відповідь з інформацією користувача
login.oauth2invalidRequest=Недійсний запит login.oauth2invalidRequest=Недійсний запит
@@ -951,6 +951,7 @@ fileToPDF.submit=Перетворити у PDF
compress.title=Стиснути compress.title=Стиснути
compress.header=Стиснути PDF compress.header=Стиснути PDF
compress.credit=Ця служба використовує qpdf для стиснення/оптимізації PDF. compress.credit=Ця служба використовує qpdf для стиснення/оптимізації PDF.
compress.grayscale.label=Застосувати відтінки сірого для стиснення
compress.selectText.1=Ручний режим - від 1 до 5 compress.selectText.1=Ручний режим - від 1 до 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Рівень оптимізації: compress.selectText.2=Рівень оптимізації:

View File

@@ -572,8 +572,8 @@ login.invalid=Tên đăng nhập hoặc mật khẩu không hợp lệ.
login.locked=Tài khoản của bạn đã bị khóa. login.locked=Tài khoản của bạn đã bị khóa.
login.signinTitle=Vui lòng đăng nhập login.signinTitle=Vui lòng đăng nhập
login.ssoSignIn=Đăng nhập qua Single Sign-on login.ssoSignIn=Đăng nhập qua Single Sign-on
login.oauth2AutoCreateDisabled=Tự động tạo người dùng OAUTH2 bị vô hiệu hóa login.oAuth2AutoCreateDisabled=Tự động tạo người dùng OAUTH2 bị vô hiệu hóa
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator. login.oAuth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Không tìm thấy yêu cầu ủy quyền login.oauth2RequestNotFound=Không tìm thấy yêu cầu ủy quyền
login.oauth2InvalidUserInfoResponse=Phản hồi thông tin người dùng không hợp lệ login.oauth2InvalidUserInfoResponse=Phản hồi thông tin người dùng không hợp lệ
login.oauth2invalidRequest=Yêu cầu không hợp lệ login.oauth2invalidRequest=Yêu cầu không hợp lệ
@@ -951,6 +951,7 @@ fileToPDF.submit=Chuyển đổi sang PDF
compress.title=Nén compress.title=Nén
compress.header=Nén PDF compress.header=Nén PDF
compress.credit=Dịch vụ này sử dụng qpdf để Nén/Tối ưu hóa PDF. compress.credit=Dịch vụ này sử dụng qpdf để Nén/Tối ưu hóa PDF.
compress.grayscale.label=Áp dụng thang độ xám để nén
compress.selectText.1=Chế độ thủ công - Từ 1 đến 5 compress.selectText.1=Chế độ thủ công - Từ 1 đến 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Mức độ tối ưu hóa: compress.selectText.2=Mức độ tối ưu hóa:

View File

@@ -572,8 +572,8 @@ login.invalid=སྤྱོད་མིང་ངམ་གསང་ཚིག་ན
login.locked=ཁྱེད་ཀྱི་ཐོ་མཛོད་ཟྭ་རྒྱག་བརྒྱབ་ཟིན། login.locked=ཁྱེད་ཀྱི་ཐོ་མཛོད་ཟྭ་རྒྱག་བརྒྱབ་ཟིན།
login.signinTitle=ནང་འཛུལ་གནང་རོགས། login.signinTitle=ནང་འཛུལ་གནང་རོགས།
login.ssoSignIn=གཅིག་གྱུར་ནང་འཛུལ་བརྒྱུད་ནས་ནང་འཛུལ། login.ssoSignIn=གཅིག་གྱུར་ནང་འཛུལ་བརྒྱུད་ནས་ནང་འཛུལ།
login.oauth2AutoCreateDisabled=OAUTH2 རང་འགུལ་སྤྱོད་མཁན་གསར་བཟོ་བཀག་སྡོམ་བྱས་ཟིན། login.oAuth2AutoCreateDisabled=OAUTH2 རང་འགུལ་སྤྱོད་མཁན་གསར་བཟོ་བཀག་སྡོམ་བྱས་ཟིན།
login.oauth2AdminBlockedUser=ད་ལྟ་ཐོ་འགོད་མ་བྱས་པའི་སྤྱོད་མཁན་གྱི་ཐོ་འགོད་དང་ནང་འཛུལ་བཀག་སྡོམ་བྱས་ཡོད། དོ་དམ་པར་འབྲེལ་བ་གནང་རོགས། login.oAuth2AdminBlockedUser=ད་ལྟ་ཐོ་འགོད་མ་བྱས་པའི་སྤྱོད་མཁན་གྱི་ཐོ་འགོད་དང་ནང་འཛུལ་བཀག་སྡོམ་བྱས་ཡོད། དོ་དམ་པར་འབྲེལ་བ་གནང་རོགས།
login.oauth2RequestNotFound=དབང་སྤྲོད་རེ་ཞུ་རྙེད་མ་བྱུང་། login.oauth2RequestNotFound=དབང་སྤྲོད་རེ་ཞུ་རྙེད་མ་བྱུང་།
login.oauth2InvalidUserInfoResponse=སྤྱོད་མཁན་གྱི་གནས་ཚུལ་ལན་འདེབས་ནོར་འཁྲུལ། login.oauth2InvalidUserInfoResponse=སྤྱོད་མཁན་གྱི་གནས་ཚུལ་ལན་འདེབས་ནོར་འཁྲུལ།
login.oauth2invalidRequest=རེ་ཞུ་ནོར་འཁྲུལ། login.oauth2invalidRequest=རེ་ཞུ་ནོར་འཁྲུལ།
@@ -951,6 +951,7 @@ fileToPDF.submit=PDF ལ་བསྒྱུར་བ།
compress.title=སྡུད་སྒྲིལ། compress.title=སྡུད་སྒྲིལ།
compress.header=PDF སྡུད་སྒྲིལ། compress.header=PDF སྡུད་སྒྲིལ།
compress.credit=ཞབས་ཞུ་འདིས་ PDF སྡུད་སྒྲིལ་/ཡར་རྒྱས་གཏོང་བའི་ཆེད་དུ་ qpdf བེད་སྤྱོད་བྱེད་པ། compress.credit=ཞབས་ཞུ་འདིས་ PDF སྡུད་སྒྲིལ་/ཡར་རྒྱས་གཏོང་བའི་ཆེད་དུ་ qpdf བེད་སྤྱོད་བྱེད་པ།
compress.grayscale.label=应用灰度进行压缩
compress.selectText.1=Manual Mode - From 1 to 4 compress.selectText.1=Manual Mode - From 1 to 4
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=Optimisation level: compress.selectText.2=Optimisation level:

View File

@@ -572,8 +572,8 @@ login.invalid=用户名或密码无效。
login.locked=您的账户已被锁定。 login.locked=您的账户已被锁定。
login.signinTitle=请登录 login.signinTitle=请登录
login.ssoSignIn=通过单点登录登录 login.ssoSignIn=通过单点登录登录
login.oauth2AutoCreateDisabled=OAuth2 自动创建用户已禁用 login.oAuth2AutoCreateDisabled=OAuth2 自动创建用户已禁用
login.oauth2AdminBlockedUser=目前已阻止未注册用户的注册或登录。请联系管理员。 login.oAuth2AdminBlockedUser=目前已阻止未注册用户的注册或登录。请联系管理员。
login.oauth2RequestNotFound=找不到验证请求 login.oauth2RequestNotFound=找不到验证请求
login.oauth2InvalidUserInfoResponse=无效的用户信息响应 login.oauth2InvalidUserInfoResponse=无效的用户信息响应
login.oauth2invalidRequest=无效请求 login.oauth2invalidRequest=无效请求
@@ -951,6 +951,7 @@ fileToPDF.submit=转换为 PDF
compress.title=压缩 compress.title=压缩
compress.header=压缩 PDF compress.header=压缩 PDF
compress.credit=此服务使用qpdf进行 PDF 压缩/优化。 compress.credit=此服务使用qpdf进行 PDF 压缩/优化。
compress.grayscale.label=应用灰度进行压缩
compress.selectText.1=手动模式 - 从 1 到 5 compress.selectText.1=手动模式 - 从 1 到 5
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images. compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=优化级别: compress.selectText.2=优化级别:

View File

@@ -138,7 +138,7 @@ analytics.settings=您可以在 config/settings.yml 檔案中變更分析功能
# NAVBAR # # NAVBAR #
############# #############
navbar.favorite=我的最愛 navbar.favorite=我的最愛
navbar.recent=New and recently updated navbar.recent=最新與近期更新
navbar.darkmode=深色模式 navbar.darkmode=深色模式
navbar.language=語言 navbar.language=語言
navbar.settings=設定 navbar.settings=設定
@@ -262,18 +262,18 @@ home.desc=您的本機一站式 PDF 解決方案。
home.searchBar=搜尋功能... home.searchBar=搜尋功能...
home.viewPdf.title=View/Edit PDF home.viewPdf.title=檢視/編輯 PDF
home.viewPdf.desc=檢視、註釋、新增文字或圖片 home.viewPdf.desc=檢視、註釋、新增文字或圖片
viewPdf.tags=檢視,閱讀,註釋,文字,圖片 viewPdf.tags=檢視,閱讀,註釋,文字,圖片
home.setFavorites=Set Favourites home.setFavorites=設定我的最愛
home.hideFavorites=Hide Favourites home.hideFavorites=隱藏我的最愛
home.showFavorites=Show Favourites home.showFavorites=顯示我的最愛
home.legacyHomepage=Old homepage home.legacyHomepage=舊版首頁
home.newHomePage=Try our new homepage! home.newHomePage=嘗試使用全新首頁!
home.alphabetical=Alphabetical home.alphabetical=按照字母排序
home.globalPopularity=Global Popularity home.globalPopularity=熱門程度
home.sortBy=Sort by: home.sortBy=排序方式:
home.multiTool.title=PDF 複合工具 home.multiTool.title=PDF 複合工具
home.multiTool.desc=合併、旋轉、重新排列和移除頁面 home.multiTool.desc=合併、旋轉、重新排列和移除頁面
@@ -572,8 +572,8 @@ login.invalid=使用者名稱或密碼無效。
login.locked=您的帳號已被鎖定。 login.locked=您的帳號已被鎖定。
login.signinTitle=請登入 login.signinTitle=請登入
login.ssoSignIn=透過 SSO 單一登入 login.ssoSignIn=透過 SSO 單一登入
login.oauth2AutoCreateDisabled=OAuth 2.0 自動建立使用者功能已停用 login.oAuth2AutoCreateDisabled=OAuth 2.0 自動建立使用者功能已停用
login.oauth2AdminBlockedUser=目前不允許未註冊的使用者註冊或登入。請聯絡系統管理員。 login.oAuth2AdminBlockedUser=目前不允許未註冊的使用者註冊或登入。請聯絡系統管理員。
login.oauth2RequestNotFound=找不到驗證請求 login.oauth2RequestNotFound=找不到驗證請求
login.oauth2InvalidUserInfoResponse=使用者資訊回應無效 login.oauth2InvalidUserInfoResponse=使用者資訊回應無效
login.oauth2invalidRequest=請求無效 login.oauth2invalidRequest=請求無效
@@ -623,7 +623,7 @@ redact.showAttatchments=顯示附件
redact.showLayers=顯示圖層(按兩下可將所有圖層重設為預設狀態) redact.showLayers=顯示圖層(按兩下可將所有圖層重設為預設狀態)
redact.colourPicker=顏色選擇器 redact.colourPicker=顏色選擇器
redact.findCurrentOutlineItem=尋找目前的大綱項目 redact.findCurrentOutlineItem=尋找目前的大綱項目
redact.applyChanges=Apply Changes redact.applyChanges=套用變更
#showJS #showJS
showJS.title=顯示 JavaScript showJS.title=顯示 JavaScript
@@ -951,6 +951,7 @@ fileToPDF.submit=轉換為 PDF
compress.title=壓縮 compress.title=壓縮
compress.header=壓縮 PDF compress.header=壓縮 PDF
compress.credit=此服務使用 qpdf 進行 PDF 壓縮/最佳化。 compress.credit=此服務使用 qpdf 進行 PDF 壓縮/最佳化。
compress.grayscale.label=應用灰階進行壓縮
compress.selectText.1=手動模式 - 從 1 到 5 compress.selectText.1=手動模式 - 從 1 到 5
compress.selectText.1.1=在最佳化等級 6 到 9 時,除了一般 PDF 壓縮外,圖片解析度也會降低以進一步減少檔案大小。較高的壓縮等級會進行更高強度的圖片壓縮(最多可壓縮到原始大小的 50%),以達到更高的壓縮率,但可能會影響圖片品質。 compress.selectText.1.1=在最佳化等級 6 到 9 時,除了一般 PDF 壓縮外,圖片解析度也會降低以進一步減少檔案大小。較高的壓縮等級會進行更高強度的圖片壓縮(最多可壓縮到原始大小的 50%),以達到更高的壓縮率,但可能會影響圖片品質。
compress.selectText.2=最佳化等級: compress.selectText.2=最佳化等級:
@@ -992,7 +993,7 @@ pdfOrganiser.mode.7=刪除第一頁
pdfOrganiser.mode.8=刪除最後一頁 pdfOrganiser.mode.8=刪除最後一頁
pdfOrganiser.mode.9=刪除第一頁和最後一頁 pdfOrganiser.mode.9=刪除第一頁和最後一頁
pdfOrganiser.mode.10=奇偶合併 pdfOrganiser.mode.10=奇偶合併
pdfOrganiser.mode.11=Duplicate all pages pdfOrganiser.mode.11=複製所有頁面
pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1 pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1
@@ -1035,7 +1036,7 @@ decrypt.success=檔案已成功解密。
multiTool-advert.message=此功能也可以在我們的<a href="{0}">複合工具頁面</a>中使用。前往查看並體驗更強大的逐頁操作介面及其他進階功能! multiTool-advert.message=此功能也可以在我們的<a href="{0}">複合工具頁面</a>中使用。前往查看並體驗更強大的逐頁操作介面及其他進階功能!
#view pdf #view pdf
viewPdf.title=View/Edit PDF viewPdf.title=檢視/編輯 PDF
viewPdf.header=檢視 PDF viewPdf.header=檢視 PDF
#pageRemover #pageRemover

View File

@@ -28,37 +28,38 @@ security:
clientId: '' # client ID for Keycloak OAuth2 clientId: '' # client ID for Keycloak OAuth2
clientSecret: '' # client secret for Keycloak OAuth2 clientSecret: '' # client secret for Keycloak OAuth2
scopes: openid, profile, email # scopes for Keycloak OAuth2 scopes: openid, profile, email # scopes for Keycloak OAuth2
useAsUsername: preferred_username # field to use as the username for Keycloak OAuth2 useAsUsername: preferred_username # field to use as the username for Keycloak OAuth2. Available options are: [email | name | given_name | family_name | preferred_name]
google: google:
clientId: '' # client ID for Google OAuth2 clientId: '' # client ID for Google OAuth2
clientSecret: '' # client secret for Google OAuth2 clientSecret: '' # client secret for Google OAuth2
scopes: https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile # scopes for Google OAuth2 scopes: email, profile # scopes for Google OAuth2
useAsUsername: email # field to use as the username for Google OAuth2 useAsUsername: email # field to use as the username for Google OAuth2. Available options are: [email | name | given_name | family_name]
github: github:
clientId: '' # client ID for GitHub OAuth2 clientId: '' # client ID for GitHub OAuth2
clientSecret: '' # client secret for GitHub OAuth2 clientSecret: '' # client secret for GitHub OAuth2
scopes: read:user # scope for GitHub OAuth2 scopes: read:user # scope for GitHub OAuth2
useAsUsername: login # field to use as the username for GitHub OAuth2 useAsUsername: login # field to use as the username for GitHub OAuth2. Available options are: [email | login | name]
issuer: '' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint issuer: '' # set to any Provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint
clientId: '' # client ID from your provider clientId: '' # client ID from your Provider
clientSecret: '' # client secret from your provider clientSecret: '' # client secret from your Provider
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
useAsUsername: email # default is 'email'; custom fields can be used as the username useAsUsername: email # default is 'email'; custom fields can be used as the username
scopes: openid, profile, email # specify the scopes for which the application will request permissions scopes: openid, profile, email # specify the scopes for which the application will request permissions
provider: google # set this to your OAuth provider's name, e.g., 'google' or 'keycloak' provider: google # set this to your OAuth Provider's name, e.g., 'google' or 'keycloak'
saml2: saml2:
enabled: false # Only enabled for paid enterprise clients (enterpriseEdition.enabled must be true) enabled: false # Only enabled for paid enterprise clients (enterpriseEdition.enabled must be true)
provider: '' # The name of your Provider
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
registrationId: stirling registrationId: stirling # The name of your Service Provider (SP) app name. Should match the name in the path for your SSO & SLO URLs
idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata # The uri for your Provider's metadata
idpSingleLogoutUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/slo/saml idpSingleLoginUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/sso/saml # The URL for initiating SSO. Provided by your Provider
idpSingleLoginUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/sso/saml idpSingleLogoutUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/slo/saml # The URL for initiating SLO. Provided by your Provider
idpIssuer: http://www.okta.com/externalKey idpIssuer: '' # The ID of your Provider
idpCert: classpath:okta.crt idpCert: classpath:okta.cert # The certificate your Provider will use to authenticate your app's SAML authentication requests. Provided by your Provider
privateKey: classpath:saml-private-key.key privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
spCert: classpath:saml-public-cert.crt spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
enterpriseEdition: enterpriseEdition:
enabled: false # set to 'true' to enable enterprise edition enabled: false # set to 'true' to enable enterprise edition
@@ -85,7 +86,7 @@ system:
showUpdateOnlyAdmin: false # only admins can see when a new update is available, depending on showUpdate it must be set to 'true' showUpdateOnlyAdmin: false # only admins can see when a new update is available, depending on showUpdate it must be set to 'true'
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template HTML files customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template HTML files
tessdataDir: /usr/share/tessdata # path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored. tessdataDir: /usr/share/tessdata # path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored.
enableAnalytics: 'true' # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true enableAnalytics: 'undefined' # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true
disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML) disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML)
datasource: datasource:
enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration

Some files were not shown because too many files have changed in this diff Show More