Csrf fix and ssoAutoLogin for enterprise users (#2653)

This pull request includes several changes to the
`SecurityConfiguration` and other related classes to enhance security
and configuration management. The most important changes involve adding
new beans, modifying logging levels, and updating dependency injections.

Enhancements to security configuration:

*
[`src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java`](diffhunk://#diff-49df1b16b72e9fcaa7d0c58f46c94ffda0033f5f5e3ddab90a88e2f9022b66f4L3-L36):
Added new dependencies and beans for `GrantedAuthoritiesMapper`,
`RelyingPartyRegistrationRepository`, and
`OpenSaml4AuthenticationRequestResolver`. Removed unused imports and
simplified the class by removing the `@Lazy` annotation from
`UserService`.
[[1]](diffhunk://#diff-49df1b16b72e9fcaa7d0c58f46c94ffda0033f5f5e3ddab90a88e2f9022b66f4L3-L36)
[[2]](diffhunk://#diff-49df1b16b72e9fcaa7d0c58f46c94ffda0033f5f5e3ddab90a88e2f9022b66f4L46-L63)
[[3]](diffhunk://#diff-49df1b16b72e9fcaa7d0c58f46c94ffda0033f5f5e3ddab90a88e2f9022b66f4L75-R52)
[[4]](diffhunk://#diff-49df1b16b72e9fcaa7d0c58f46c94ffda0033f5f5e3ddab90a88e2f9022b66f4R66-L98)
[[5]](diffhunk://#diff-49df1b16b72e9fcaa7d0c58f46c94ffda0033f5f5e3ddab90a88e2f9022b66f4L109-R85)
[[6]](diffhunk://#diff-49df1b16b72e9fcaa7d0c58f46c94ffda0033f5f5e3ddab90a88e2f9022b66f4R96-R98)

Logging improvements:

*
[`src/main/java/stirling/software/SPDF/EE/KeygenLicenseVerifier.java`](diffhunk://#diff-742f789731a32cb5aa20f7067ef18049002eec2a4909ef6f240d2a26bdcb53c4L97-R97):
Changed the logging level from `info` to `debug` for the license
validation response body to reduce log verbosity in production.

Configuration updates:

*
[`src/main/java/stirling/software/SPDF/EE/EEAppConfig.java`](diffhunk://#diff-d842c2a4cf43f37ab5edcd644b19a51d614cb0e39963789e1c7e9fb28ddc1de8R30-R34):
Added a new bean `ssoAutoLogin` to manage single sign-on auto-login
configuration in the enterprise edition.

These changes collectively enhance the security configuration and
logging management of the application.

Please provide a summary of the changes, including relevant motivation
and context.

Closes #(issue_number)

## Checklist

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated
relevant docs on [Stirling-PDFs doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
- [ ] 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)
This commit is contained in:
Anthony Stirling
2025-01-09 14:40:51 +00:00
committed by GitHub
parent 1ed1b17510
commit b98f8627ac
15 changed files with 532 additions and 403 deletions

View File

@@ -267,7 +267,7 @@
</div>
<script th:inline="javascript">
document.addEventListener("DOMContentLoaded", function() {
document.addEventListener("DOMContentLoaded", async function() {
const settingsTableBody = document.querySelector("#settingsTable tbody");
/*<![CDATA[*/
@@ -306,28 +306,38 @@
location.reload(); // Refresh the page after sync
});
document.getElementById('syncToAccount').addEventListener('click', function() {
document.getElementById('syncToAccount').addEventListener('click', async function() {
/*<![CDATA[*/
const urlUpdateUserSettings = /*[[@{/api/v1/user/updateUserSettings}]]*/ "/api/v1/user/updateUserSettings";
/*]]>*/
let form = document.createElement("form");
form.method = "POST";
form.action = urlUpdateUserSettings; // Your endpoint URL
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if(key !== 'debug' && key !== '0' && key !== '1' && !key.includes('pdfjs') && !key.includes('posthog') && !key.includes('pageViews')) { // Only send non-ignored keys
let hiddenField = document.createElement("input");
hiddenField.type = "hidden";
hiddenField.name = key;
hiddenField.value = localStorage.getItem(key);
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
form.submit();
});
const urlUpdateUserSettings = /*[[@{/api/v1/user/updateUserSettings}]]*/ "/api/v1/user/updateUserSettings";
/*]]>*/
let settings = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if(key !== 'debug' && key !== '0' && key !== '1' && !key.includes('pdfjs') && !key.includes('posthog') && !key.includes('pageViews')) {
settings[key] = localStorage.getItem(key);
}
}
try {
const response = await window.fetchWithCsrf(urlUpdateUserSettings, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(settings)
});
if (response.ok) {
location.reload();
} else {
alert('Error syncing settings to account');
}
} catch (error) {
console.error('Error:', error);
alert('Error syncing settings to account');
}
});
});
</script>

View File

@@ -11,48 +11,88 @@
<div class="your-container-class"></div>
<div class="container-flex">
<main class="form-signin">
<script>
document.addEventListener('modeChanged', function(e) {
var mode = e.detail;
document.body.classList.remove("light-mode", "dark-mode", "rainbow-mode"); // remove all mode classes first
switch (mode) {
case "on":
document.body.classList.add("dark-mode");
break;
case "off":
document.body.classList.add("light-mode");
break;
case "rainbow":
document.body.classList.add("rainbow-mode");
break;
}
});
document.addEventListener('DOMContentLoaded', function() {
const defaultLocale = getStoredOrDefaultLocale();
checkUserLanguage(defaultLocale);
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
let activeItem;
for (let i = 0; i < dropdownItems.length; i++) {
const item = dropdownItems[i];
item.classList.remove('active');
if (item.dataset.bsLanguageCode === defaultLocale) {
item.classList.add('active');
activeItem = item;
}
item.addEventListener('click', handleDropdownItemClick);
}
const dropdown = document.getElementById('languageDropdown');
if (activeItem) {
dropdown.innerHTML = activeItem.innerHTML; // This will set the dropdown button's content to the active language's flag and name
}
});
<script th:inline="javascript">
const redirectAttempts = parseInt(localStorage.getItem('ssoRedirectAttempts') || '0');
const urlParams = new URLSearchParams(window.location.search);
const hasRedirectError = urlParams.has('error');
const hasLogout = urlParams.has('logout');
const hasMessage = urlParams.has('message');
const MAX_REDIRECT_ATTEMPTS = 3;
document.addEventListener('modeChanged', function(e) {
var mode = e.detail;
document.body.classList.remove("light-mode", "dark-mode", "rainbow-mode"); // remove all mode classes first
switch (mode) {
case "on":
document.body.classList.add("dark-mode");
break;
case "off":
document.body.classList.add("light-mode");
break;
case "rainbow":
document.body.classList.add("rainbow-mode");
break;
}
});
document.addEventListener('DOMContentLoaded', function() {
const runningEE = [[${@runningEE}]];
const SSOAutoLogin = [[${@SSOAutoLogin}]];
const loginMethod = [[${loginMethod}]];
const providerList = [[${providerlist}]];
const shouldAutoRedirect = !hasRedirectError &&
!hasLogout &&
!hasMessage &&
redirectAttempts < MAX_REDIRECT_ATTEMPTS &&
loginMethod !== 'normal' && runningEE && SSOAutoLogin;
console.log('Should redirect:', shouldAutoRedirect, {
'No error': !hasRedirectError,
'No logout': !hasLogout,
'No message': !hasMessage,
'Under max attempts': redirectAttempts < MAX_REDIRECT_ATTEMPTS,
'Is OAuth2': loginMethod === 'oauth2'
});
if (shouldAutoRedirect && providerList && Object.keys(providerList).length > 0) {
localStorage.setItem('ssoRedirectAttempts', redirectAttempts + 1);
const firstProvider = Object.keys(providerList)[0];
window.location.href = firstProvider;
}
// Reset redirect attempts if successful login or after 1 hour
const lastAttemptTime = parseInt(localStorage.getItem('lastRedirectAttempt') || '0');
if (Date.now() - lastAttemptTime > 3600000) { // 1 hour
localStorage.setItem('ssoRedirectAttempts', '0');
}
localStorage.setItem('lastRedirectAttempt', Date.now().toString());
const defaultLocale = getStoredOrDefaultLocale();
checkUserLanguage(defaultLocale);
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
let activeItem;
for (let i = 0; i < dropdownItems.length; i++) {
const item = dropdownItems[i];
item.classList.remove('active');
if (item.dataset.bsLanguageCode === defaultLocale) {
item.classList.add('active');
activeItem = item;
}
item.addEventListener('click', handleDropdownItemClick);
}
const dropdown = document.getElementById('languageDropdown');
if (activeItem) {
dropdown.innerHTML = activeItem.innerHTML; // This will set the dropdown button's content to the active language's flag and name
}
});
</script>
<div class="text-center">
<img class="my-4" th:src="@{'/favicon.svg'}" alt="favicon" width="144" height="144">