Merge remote-tracking branch 'origin/main' into cucumber
This commit is contained in:
commit
48bd060d6e
57 changed files with 1668 additions and 817 deletions
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
@ -10,4 +10,4 @@ liberapay: # Replace with a single Liberapay username
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
otechie: # Replace with a single Otechie username
|
otechie: # Replace with a single Otechie username
|
||||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
custom: ['https://paypal.me/froodleplex?country.x=GB&locale.x=en_GB'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
custom: ['https://www.paypal.com/donate/?hosted_button_id=MN7JPG5G6G3JL'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
|
|
|
@ -29,7 +29,7 @@ If you would like to add or modify a translation, please see [How to add new lan
|
||||||
|
|
||||||
## Docs
|
## Docs
|
||||||
|
|
||||||
Documentation for Stirling-PDF is handled in a seperate repository. Please see [Docs repository](https://github.com/Stirling-Tools/Stirling-Tools.github.io) or use "edit this page"-button at the bottom of each page at [https://stirlingtools.com/docs/](https://stirlingtools.com/docs/).
|
Documentation for Stirling-PDF is handled in a separate repository. Please see [Docs repository](https://github.com/Stirling-Tools/Stirling-Tools.github.io) or use "edit this page"-button at the bottom of each page at [https://stirlingtools.com/docs/](https://stirlingtools.com/docs/).
|
||||||
|
|
||||||
## Fixing Bugs or Adding a New Feature
|
## Fixing Bugs or Adding a New Feature
|
||||||
|
|
||||||
|
|
48
README.md
48
README.md
|
@ -159,38 +159,39 @@ Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR
|
||||||
|
|
||||||
## Supported Languages
|
## Supported Languages
|
||||||
|
|
||||||
Stirling PDF currently supports 27!
|
Stirling PDF currently supports 28!
|
||||||
|
|
||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| ------------------------------------------- | -------------------------------------- |
|
| ------------------------------------------- | -------------------------------------- |
|
||||||
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
|
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
|
||||||
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
|
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
|
||||||
| Arabic (العربية) (ar_AR) | ![41%](https://geps.dev/progress/41) |
|
| Arabic (العربية) (ar_AR) | ![41%](https://geps.dev/progress/41) |
|
||||||
| German (Deutsch) (de_DE) | ![97%](https://geps.dev/progress/97) |
|
| German (Deutsch) (de_DE) | ![100%](https://geps.dev/progress/100) |
|
||||||
| French (Français) (fr_FR) | ![94%](https://geps.dev/progress/94) |
|
| French (Français) (fr_FR) | ![94%](https://geps.dev/progress/94) |
|
||||||
| Spanish (Español) (es_ES) | ![97%](https://geps.dev/progress/97) |
|
| Spanish (Español) (es_ES) | ![96%](https://geps.dev/progress/96) |
|
||||||
| Simplified Chinese (简体中文) (zh_CN) | ![96%](https://geps.dev/progress/96) |
|
| Simplified Chinese (简体中文) (zh_CN) | ![96%](https://geps.dev/progress/96) |
|
||||||
| Traditional Chinese (繁體中文) (zh_TW) | ![96%](https://geps.dev/progress/96) |
|
| Traditional Chinese (繁體中文) (zh_TW) | ![95%](https://geps.dev/progress/95) |
|
||||||
| Catalan (Català) (ca_CA) | ![50%](https://geps.dev/progress/50) |
|
| Catalan (Català) (ca_CA) | ![49%](https://geps.dev/progress/49) |
|
||||||
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
|
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
|
||||||
| Swedish (Svenska) (sv_SE) | ![41%](https://geps.dev/progress/41) |
|
| Swedish (Svenska) (sv_SE) | ![41%](https://geps.dev/progress/41) |
|
||||||
| Polish (Polski) (pl_PL) | ![43%](https://geps.dev/progress/43) |
|
| Polish (Polski) (pl_PL) | ![43%](https://geps.dev/progress/43) |
|
||||||
| Romanian (Română) (ro_RO) | ![40%](https://geps.dev/progress/40) |
|
| Romanian (Română) (ro_RO) | ![40%](https://geps.dev/progress/40) |
|
||||||
| Korean (한국어) (ko_KR) | ![89%](https://geps.dev/progress/89) |
|
| Korean (한국어) (ko_KR) | ![88%](https://geps.dev/progress/88) |
|
||||||
| Portuguese Brazilian (Português) (pt_BR) | ![62%](https://geps.dev/progress/62) |
|
| Portuguese Brazilian (Português) (pt_BR) | ![62%](https://geps.dev/progress/62) |
|
||||||
| Russian (Русский) (ru_RU) | ![89%](https://geps.dev/progress/89) |
|
| Russian (Русский) (ru_RU) | ![88%](https://geps.dev/progress/88) |
|
||||||
| Basque (Euskara) (eu_ES) | ![65%](https://geps.dev/progress/65) |
|
| Basque (Euskara) (eu_ES) | ![64%](https://geps.dev/progress/64) |
|
||||||
| Japanese (日本語) (ja_JP) | ![89%](https://geps.dev/progress/89) |
|
| Japanese (日本語) (ja_JP) | ![88%](https://geps.dev/progress/88) |
|
||||||
| Dutch (Nederlands) (nl_NL) | ![86%](https://geps.dev/progress/86) |
|
| Dutch (Nederlands) (nl_NL) | ![86%](https://geps.dev/progress/86) |
|
||||||
| Greek (Ελληνικά) (el_GR) | ![87%](https://geps.dev/progress/87) |
|
| Greek (Ελληνικά) (el_GR) | ![86%](https://geps.dev/progress/86) |
|
||||||
| Turkish (Türkçe) (tr_TR) | ![99%](https://geps.dev/progress/99) |
|
| Turkish (Türkçe) (tr_TR) | ![99%](https://geps.dev/progress/99) |
|
||||||
| Indonesia (Bahasa Indonesia) (id_ID) | ![80%](https://geps.dev/progress/80) |
|
| Indonesia (Bahasa Indonesia) (id_ID) | ![79%](https://geps.dev/progress/79) |
|
||||||
| Hindi (हिंदी) (hi_IN) | ![81%](https://geps.dev/progress/81) |
|
| Hindi (हिंदी) (hi_IN) | ![80%](https://geps.dev/progress/80) |
|
||||||
| Hungarian (Magyar) (hu_HU) | ![79%](https://geps.dev/progress/79) |
|
| Hungarian (Magyar) (hu_HU) | ![79%](https://geps.dev/progress/79) |
|
||||||
| Bulgarian (Български) (bg_BG) | ![96%](https://geps.dev/progress/96) |
|
| Bulgarian (Български) (bg_BG) | ![96%](https://geps.dev/progress/96) |
|
||||||
| Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![82%](https://geps.dev/progress/82) |
|
| Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![81%](https://geps.dev/progress/81) |
|
||||||
| Ukrainian (Українська) (uk_UA) | ![88%](https://geps.dev/progress/88) |
|
| Ukrainian (Українська) (uk_UA) | ![87%](https://geps.dev/progress/87) |
|
||||||
| Slovakian (Slovensky) (sk_SK) | ![96%](https://geps.dev/progress/96) |
|
| Slovakian (Slovensky) (sk_SK) | ![96%](https://geps.dev/progress/96) |
|
||||||
|
| Czech (Česky) (cs_CZ) | ![94%](https://geps.dev/progress/94) |
|
||||||
|
|
||||||
## Contributing (creating issues, translations, fixing bugs, etc.)
|
## Contributing (creating issues, translations, fixing bugs, etc.)
|
||||||
|
|
||||||
|
@ -226,7 +227,7 @@ security:
|
||||||
loginAttemptCount: 5 # lock user account after 5 tries
|
loginAttemptCount: 5 # lock user account after 5 tries
|
||||||
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
|
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
|
||||||
# initialLogin:
|
# initialLogin:
|
||||||
# username: "admin" # Initial username for the first login (these are defaulted)
|
# username: "admin" # Initial username for the first login
|
||||||
# password: "stirling" # Initial password for the first login
|
# password: "stirling" # Initial password for the first login
|
||||||
# oauth2:
|
# oauth2:
|
||||||
# enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work)
|
# enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work)
|
||||||
|
@ -237,6 +238,23 @@ security:
|
||||||
# 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'
|
||||||
|
# client:
|
||||||
|
# google:
|
||||||
|
# clientId: "" # Client ID 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
|
||||||
|
# useAsUsername: "email" # Field to use as the username for Google OAuth2
|
||||||
|
# github:
|
||||||
|
# clientId: "" # Client ID for GitHub OAuth2
|
||||||
|
# clientSecret: "" # Client Secret for GitHub OAuth2
|
||||||
|
# scopes: "read:user" # Scope for GitHub OAuth2
|
||||||
|
# useAsUsername: "login" # Field to use as the username for GitHub OAuth2
|
||||||
|
# keycloak:
|
||||||
|
# issuer: "http://192.168.0.123:8888/realms/stirling-pdf" # URL of the Keycloak realm's OpenID Connect Discovery endpoint
|
||||||
|
# clientId: "stirling-pdf" # Client ID for Keycloak OAuth2
|
||||||
|
# clientSecret: "" # Client Secret for Keycloak OAuth2
|
||||||
|
# scopes: "openid, profile, email" # Scopes for Keycloak OAuth2
|
||||||
|
# useAsUsername: "email" # Field to use as the username for Keycloak OAuth2
|
||||||
|
|
||||||
system:
|
system:
|
||||||
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
|
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
|
||||||
|
|
|
@ -12,7 +12,7 @@ plugins {
|
||||||
import com.github.jk1.license.render.*
|
import com.github.jk1.license.render.*
|
||||||
|
|
||||||
group = 'stirling.software'
|
group = 'stirling.software'
|
||||||
version = '0.24.5'
|
version = '0.24.6'
|
||||||
|
|
||||||
//17 is lowest but we support and recommend 21
|
//17 is lowest but we support and recommend 21
|
||||||
sourceCompatibility = '17'
|
sourceCompatibility = '17'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
appVersion: 0.24.5
|
appVersion: 0.24.6
|
||||||
description: locally hosted web application that allows you to perform various operations
|
description: locally hosted web application that allows you to perform various operations
|
||||||
on PDF files
|
on PDF files
|
||||||
home: https://github.com/Stirling-Tools/Stirling-PDF
|
home: https://github.com/Stirling-Tools/Stirling-PDF
|
||||||
|
|
|
@ -13,6 +13,11 @@ ignore = [
|
||||||
'language.direction',
|
'language.direction',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[cs_CZ]
|
||||||
|
ignore = [
|
||||||
|
'language.direction',
|
||||||
|
]
|
||||||
|
|
||||||
[de_DE]
|
[de_DE]
|
||||||
ignore = [
|
ignore = [
|
||||||
'AddStampRequest.alphabet',
|
'AddStampRequest.alphabet',
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class ConfigInitializer
|
||||||
Files.exists(userPath) ? Files.readAllLines(userPath) : new ArrayList<>();
|
Files.exists(userPath) ? Files.readAllLines(userPath) : new ArrayList<>();
|
||||||
|
|
||||||
List<String> resultLines = new ArrayList<>();
|
List<String> resultLines = new ArrayList<>();
|
||||||
|
int position = 0;
|
||||||
for (String templateLine : templateLines) {
|
for (String templateLine : templateLines) {
|
||||||
// Check if the line is a comment
|
// Check if the line is a comment
|
||||||
if (templateLine.trim().startsWith("#")) {
|
if (templateLine.trim().startsWith("#")) {
|
||||||
|
@ -66,7 +66,7 @@ public class ConfigInitializer
|
||||||
if (!entry.isEmpty()) {
|
if (!entry.isEmpty()) {
|
||||||
// Check if this comment has been uncommented in userLines
|
// Check if this comment has been uncommented in userLines
|
||||||
String key = entry.split(":")[0].trim();
|
String key = entry.split(":")[0].trim();
|
||||||
addLine(resultLines, userLines, templateLine, key);
|
addLine(resultLines, userLines, templateLine, key, position);
|
||||||
} else {
|
} else {
|
||||||
resultLines.add(templateLine);
|
resultLines.add(templateLine);
|
||||||
}
|
}
|
||||||
|
@ -74,12 +74,13 @@ public class ConfigInitializer
|
||||||
// Check if the line is a key-value pair
|
// Check if the line is a key-value pair
|
||||||
else if (templateLine.contains(":")) {
|
else if (templateLine.contains(":")) {
|
||||||
String key = templateLine.split(":")[0].trim();
|
String key = templateLine.split(":")[0].trim();
|
||||||
addLine(resultLines, userLines, templateLine, key);
|
addLine(resultLines, userLines, templateLine, key, position);
|
||||||
}
|
}
|
||||||
// Handle empty lines
|
// Handle empty lines
|
||||||
else if (templateLine.trim().length() == 0) {
|
else if (templateLine.trim().length() == 0) {
|
||||||
resultLines.add("");
|
resultLines.add("");
|
||||||
}
|
}
|
||||||
|
position++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the result to the user settings file
|
// Write the result to the user settings file
|
||||||
|
@ -94,11 +95,16 @@ public class ConfigInitializer
|
||||||
|
|
||||||
// TODO check parent value instead of just indent lines for duplicate keys (like enabled etc)
|
// TODO check parent value instead of just indent lines for duplicate keys (like enabled etc)
|
||||||
private static void addLine(
|
private static void addLine(
|
||||||
List<String> resultLines, List<String> userLines, String templateLine, String key) {
|
List<String> resultLines,
|
||||||
|
List<String> userLines,
|
||||||
|
String templateLine,
|
||||||
|
String key,
|
||||||
|
int position) {
|
||||||
boolean added = false;
|
boolean added = false;
|
||||||
int templateIndentationLevel = getIndentationLevel(templateLine);
|
int templateIndentationLevel = getIndentationLevel(templateLine);
|
||||||
|
int pos = 0;
|
||||||
for (String settingsLine : userLines) {
|
for (String settingsLine : userLines) {
|
||||||
if (settingsLine.trim().startsWith(key + ":")) {
|
if (settingsLine.trim().startsWith(key + ":") && position == pos) {
|
||||||
int settingsIndentationLevel = getIndentationLevel(settingsLine);
|
int settingsIndentationLevel = getIndentationLevel(settingsLine);
|
||||||
// Check if it is correct settingsLine and has the same parent as templateLine
|
// Check if it is correct settingsLine and has the same parent as templateLine
|
||||||
if (settingsIndentationLevel == templateIndentationLevel) {
|
if (settingsIndentationLevel == templateIndentationLevel) {
|
||||||
|
@ -107,6 +113,7 @@ public class ConfigInitializer
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pos++;
|
||||||
}
|
}
|
||||||
if (!added) {
|
if (!added) {
|
||||||
resultLines.add(templateLine);
|
resultLines.add(templateLine);
|
||||||
|
|
|
@ -2,6 +2,8 @@ package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
@ -36,7 +38,11 @@ import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationS
|
||||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2LogoutSuccessHandler;
|
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2LogoutSuccessHandler;
|
||||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
|
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.GithubProvider;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.GoogleProvider;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.KeycloakProvider;
|
||||||
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.repository.JPATokenRepositoryImpl;
|
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
||||||
|
|
||||||
|
@ -47,6 +53,8 @@ public class SecurityConfiguration {
|
||||||
|
|
||||||
@Autowired private CustomUserDetailsService userDetailsService;
|
@Autowired private CustomUserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public PasswordEncoder passwordEncoder() {
|
public PasswordEncoder passwordEncoder() {
|
||||||
return new BCryptPasswordEncoder();
|
return new BCryptPasswordEncoder();
|
||||||
|
@ -140,6 +148,7 @@ public class SecurityConfiguration {
|
||||||
|| trimmedUri.startsWith("/images/")
|
|| trimmedUri.startsWith("/images/")
|
||||||
|| trimmedUri.startsWith("/public/")
|
|| trimmedUri.startsWith("/public/")
|
||||||
|| trimmedUri.startsWith("/css/")
|
|| trimmedUri.startsWith("/css/")
|
||||||
|
|| trimmedUri.startsWith("/fonts/")
|
||||||
|| trimmedUri.startsWith("/js/")
|
|| trimmedUri.startsWith("/js/")
|
||||||
|| trimmedUri.startsWith(
|
|| trimmedUri.startsWith(
|
||||||
"/api/v1/info/status");
|
"/api/v1/info/status");
|
||||||
|
@ -150,7 +159,8 @@ public class SecurityConfiguration {
|
||||||
.authenticationProvider(authenticationProvider());
|
.authenticationProvider(authenticationProvider());
|
||||||
|
|
||||||
// Handle OAUTH2 Logins
|
// Handle OAUTH2 Logins
|
||||||
if (applicationProperties.getSecurity().getOAUTH2().getEnabled()) {
|
if (applicationProperties.getSecurity().getOAUTH2() != null
|
||||||
|
&& applicationProperties.getSecurity().getOAUTH2().getEnabled()) {
|
||||||
|
|
||||||
http.oauth2Login(
|
http.oauth2Login(
|
||||||
oauth2 ->
|
oauth2 ->
|
||||||
|
@ -181,9 +191,10 @@ public class SecurityConfiguration {
|
||||||
.logout(
|
.logout(
|
||||||
logout ->
|
logout ->
|
||||||
logout.logoutSuccessHandler(
|
logout.logoutSuccessHandler(
|
||||||
new CustomOAuth2LogoutSuccessHandler(
|
new CustomOAuth2LogoutSuccessHandler(
|
||||||
this.applicationProperties,
|
this.applicationProperties,
|
||||||
sessionRegistry())));
|
sessionRegistry()))
|
||||||
|
.invalidateHttpSession(true));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
http.csrf(csrf -> csrf.disable())
|
http.csrf(csrf -> csrf.disable())
|
||||||
|
@ -200,19 +211,127 @@ public class SecurityConfiguration {
|
||||||
havingValue = "true",
|
havingValue = "true",
|
||||||
matchIfMissing = false)
|
matchIfMissing = false)
|
||||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||||
return new InMemoryClientRegistrationRepository(this.oidcClientRegistration());
|
List<ClientRegistration> registrations = new ArrayList<>();
|
||||||
|
|
||||||
|
githubClientRegistration().ifPresent(registrations::add);
|
||||||
|
oidcClientRegistration().ifPresent(registrations::add);
|
||||||
|
googleClientRegistration().ifPresent(registrations::add);
|
||||||
|
keycloakClientRegistration().ifPresent(registrations::add);
|
||||||
|
|
||||||
|
if (registrations.isEmpty()) {
|
||||||
|
logger.error("At least one OAuth2 provider must be configured");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InMemoryClientRegistrationRepository(registrations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientRegistration oidcClientRegistration() {
|
private Optional<ClientRegistration> googleClientRegistration() {
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
return ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
|
if (oauth == null || !oauth.getEnabled()) {
|
||||||
.registrationId("oidc")
|
return Optional.empty();
|
||||||
.clientId(oauth.getClientId())
|
}
|
||||||
.clientSecret(oauth.getClientSecret())
|
Client client = oauth.getClient();
|
||||||
.scope(oauth.getScopes())
|
if (client == null) {
|
||||||
.userNameAttributeName(oauth.getUseAsUsername())
|
return Optional.empty();
|
||||||
.clientName("OIDC")
|
}
|
||||||
.build();
|
GoogleProvider google = client.getGoogle();
|
||||||
|
return google != null && google.isSettingsValid()
|
||||||
|
? Optional.of(
|
||||||
|
ClientRegistration.withRegistrationId("google")
|
||||||
|
.clientId(google.getClientId())
|
||||||
|
.clientSecret(google.getClientSecret())
|
||||||
|
.scope(google.getScopes())
|
||||||
|
.authorizationUri(google.getAuthorizationuri())
|
||||||
|
.tokenUri(google.getTokenuri())
|
||||||
|
.userInfoUri(google.getUserinfouri())
|
||||||
|
.userNameAttributeName(google.getUseAsUsername())
|
||||||
|
.clientName("Google")
|
||||||
|
.redirectUri("{baseUrl}/login/oauth2/code/google")
|
||||||
|
.authorizationGrantType(
|
||||||
|
org.springframework.security.oauth2.core
|
||||||
|
.AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.build())
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ClientRegistration> keycloakClientRegistration() {
|
||||||
|
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
|
if (oauth == null || !oauth.getEnabled()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
Client client = oauth.getClient();
|
||||||
|
if (client == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
KeycloakProvider keycloak = client.getKeycloak();
|
||||||
|
|
||||||
|
return keycloak != null && keycloak.isSettingsValid()
|
||||||
|
? Optional.of(
|
||||||
|
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
|
||||||
|
.registrationId("keycloak")
|
||||||
|
.clientId(keycloak.getClientId())
|
||||||
|
.clientSecret(keycloak.getClientSecret())
|
||||||
|
.scope(keycloak.getScopes())
|
||||||
|
.userNameAttributeName(keycloak.getUseAsUsername())
|
||||||
|
.clientName("Keycloak")
|
||||||
|
.build())
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ClientRegistration> githubClientRegistration() {
|
||||||
|
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
|
if (oauth == null || !oauth.getEnabled()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
Client client = oauth.getClient();
|
||||||
|
if (client == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
GithubProvider github = client.getGithub();
|
||||||
|
return github != null && github.isSettingsValid()
|
||||||
|
? Optional.of(
|
||||||
|
ClientRegistration.withRegistrationId("github")
|
||||||
|
.clientId(github.getClientId())
|
||||||
|
.clientSecret(github.getClientSecret())
|
||||||
|
.scope(github.getScopes())
|
||||||
|
.authorizationUri(github.getAuthorizationuri())
|
||||||
|
.tokenUri(github.getTokenuri())
|
||||||
|
.userInfoUri(github.getUserinfouri())
|
||||||
|
.userNameAttributeName(github.getUseAsUsername())
|
||||||
|
.clientName("GitHub")
|
||||||
|
.redirectUri("{baseUrl}/login/oauth2/code/github")
|
||||||
|
.authorizationGrantType(
|
||||||
|
org.springframework.security.oauth2.core
|
||||||
|
.AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.build())
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ClientRegistration> oidcClientRegistration() {
|
||||||
|
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
|
if (oauth == null
|
||||||
|
|| oauth.getIssuer() == null
|
||||||
|
|| oauth.getIssuer().isEmpty()
|
||||||
|
|| oauth.getClientId() == null
|
||||||
|
|| oauth.getClientId().isEmpty()
|
||||||
|
|| oauth.getClientSecret() == null
|
||||||
|
|| oauth.getClientSecret().isEmpty()
|
||||||
|
|| oauth.getScopes() == null
|
||||||
|
|| oauth.getScopes().isEmpty()
|
||||||
|
|| oauth.getUseAsUsername() == null
|
||||||
|
|| oauth.getUseAsUsername().isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(
|
||||||
|
ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
|
||||||
|
.registrationId("oidc")
|
||||||
|
.clientId(oauth.getClientId())
|
||||||
|
.clientSecret(oauth.getClientSecret())
|
||||||
|
.scope(oauth.getScopes())
|
||||||
|
.userNameAttributeName(oauth.getUseAsUsername())
|
||||||
|
.clientName("OIDC")
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -101,6 +101,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||||
contextPath + "/images/",
|
contextPath + "/images/",
|
||||||
contextPath + "/public/",
|
contextPath + "/public/",
|
||||||
contextPath + "/css/",
|
contextPath + "/css/",
|
||||||
|
contextPath + "/fonts/",
|
||||||
contextPath + "/js/",
|
contextPath + "/js/",
|
||||||
contextPath + "/pdfjs/",
|
contextPath + "/pdfjs/",
|
||||||
contextPath + "/api/v1/info/status",
|
contextPath + "/api/v1/info/status",
|
||||||
|
|
|
@ -41,6 +41,7 @@ public class CustomOAuth2AuthenticationFailureHandler
|
||||||
} else if (exception instanceof LockedException) {
|
} else if (exception instanceof LockedException) {
|
||||||
logger.error("Account locked: ", exception);
|
logger.error("Account locked: ", exception);
|
||||||
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
|
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
logger.error("Unhandled authentication exception", exception);
|
logger.error("Unhandled authentication exception", exception);
|
||||||
super.onAuthenticationFailure(request, response, exception);
|
super.onAuthenticationFailure(request, response, exception);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.session.SessionRegistry;
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
|
@ -14,6 +15,8 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
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.Provider;
|
||||||
|
import stirling.software.SPDF.utils.UrlUtils;
|
||||||
|
|
||||||
public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||||
|
|
||||||
|
@ -33,11 +36,30 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
||||||
public void onLogoutSuccess(
|
public void onLogoutSuccess(
|
||||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
|
|
||||||
String param = "logout=true";
|
String param = "logout=true";
|
||||||
|
String registrationId = null;
|
||||||
|
String issuer = null;
|
||||||
|
String clientId = null;
|
||||||
|
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
String provider = oauth.getProvider() != null ? oauth.getProvider() : "";
|
|
||||||
|
if (authentication instanceof OAuth2AuthenticationToken) {
|
||||||
|
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
|
||||||
|
registrationId = oauthToken.getAuthorizedClientRegistrationId();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Provider provider = oauth.getClient().get(registrationId);
|
||||||
|
issuer = provider.getIssuer();
|
||||||
|
clientId = provider.getClientId();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
registrationId = oauth.getProvider() != null ? oauth.getProvider() : "";
|
||||||
|
issuer = oauth.getIssuer();
|
||||||
|
clientId = oauth.getClientId();
|
||||||
|
}
|
||||||
|
|
||||||
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
|
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
|
||||||
param = "erroroauth=oauth2AuthenticationErrorWeb";
|
param = "erroroauth=oauth2AuthenticationErrorWeb";
|
||||||
|
@ -49,36 +71,46 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
||||||
param = "error=oauth2AutoCreateDisabled";
|
param = "error=oauth2AutoCreateDisabled";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String redirect_url = UrlUtils.getOrigin(request) + "/login?" + param;
|
||||||
|
|
||||||
HttpSession session = request.getSession(false);
|
HttpSession session = request.getSession(false);
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
String sessionId = session.getId();
|
String sessionId = session.getId();
|
||||||
sessionRegistry.removeSessionInformation(sessionId);
|
sessionRegistry.removeSessionInformation(sessionId);
|
||||||
session.invalidate();
|
session.invalidate();
|
||||||
logger.debug("Session invalidated: " + sessionId);
|
logger.info("Session invalidated: " + sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (provider) {
|
switch (registrationId) {
|
||||||
case "keycloak":
|
case "keycloak":
|
||||||
|
// Add Keycloak specific logout URL if needed
|
||||||
String logoutUrl =
|
String logoutUrl =
|
||||||
oauth.getIssuer()
|
issuer
|
||||||
+ "/protocol/openid-connect/logout"
|
+ "/protocol/openid-connect/logout"
|
||||||
+ "?client_id="
|
+ "?client_id="
|
||||||
+ oauth.getClientId()
|
+ clientId
|
||||||
+ "&post_logout_redirect_uri="
|
+ "&post_logout_redirect_uri="
|
||||||
+ response.encodeRedirectURL(
|
+ response.encodeRedirectURL(redirect_url);
|
||||||
request.getScheme()
|
logger.info("Redirecting to Keycloak logout URL: " + logoutUrl);
|
||||||
+ "://"
|
|
||||||
+ request.getHeader("host")
|
|
||||||
+ "/login?"
|
|
||||||
+ param);
|
|
||||||
logger.debug("Redirecting to Keycloak logout URL: " + logoutUrl);
|
|
||||||
response.sendRedirect(logoutUrl);
|
response.sendRedirect(logoutUrl);
|
||||||
break;
|
break;
|
||||||
|
case "github":
|
||||||
|
// Add GitHub specific logout URL if needed
|
||||||
|
String githubLogoutUrl = "https://github.com/logout";
|
||||||
|
logger.info("Redirecting to GitHub logout URL: " + githubLogoutUrl);
|
||||||
|
response.sendRedirect(githubLogoutUrl);
|
||||||
|
break;
|
||||||
case "google":
|
case "google":
|
||||||
// Add Google specific logout URL if needed
|
// Add Google specific logout URL if needed
|
||||||
|
// String googleLogoutUrl =
|
||||||
|
// "https://accounts.google.com/Logout?continue=https://appengine.google.com/_ah/logout?continue="
|
||||||
|
// + response.encodeRedirectURL(redirect_url);
|
||||||
|
// logger.info("Redirecting to Google logout URL: " + googleLogoutUrl);
|
||||||
|
// response.sendRedirect(googleLogoutUrl);
|
||||||
|
// break;
|
||||||
default:
|
default:
|
||||||
String redirectUrl = request.getContextPath() + "/login?" + param;
|
String redirectUrl = request.getContextPath() + "/login?" + param;
|
||||||
logger.debug("Redirecting to default logout URL: " + redirectUrl);
|
logger.info("Redirecting to default logout URL: " + redirectUrl);
|
||||||
response.sendRedirect(redirectUrl);
|
response.sendRedirect(redirectUrl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
package stirling.software.SPDF.config.security.oauth2;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
|
||||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
|
||||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
|
||||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
|
||||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
|
||||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
|
||||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
|
||||||
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
|
||||||
|
|
||||||
public class CustomOAuthUserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CustomOAuthUserService.class);
|
|
||||||
|
|
||||||
private final OidcUserService delegate = new OidcUserService();
|
|
||||||
|
|
||||||
private ApplicationProperties applicationProperties;
|
|
||||||
|
|
||||||
public CustomOAuthUserService(ApplicationProperties applicationProperties) {
|
|
||||||
this.applicationProperties = applicationProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
|
|
||||||
String usernameAttribute =
|
|
||||||
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername();
|
|
||||||
try {
|
|
||||||
|
|
||||||
OidcUser user = delegate.loadUser(userRequest);
|
|
||||||
Map<String, Object> attributes = new HashMap<>(user.getAttributes());
|
|
||||||
|
|
||||||
// Ensure the preferred username attribute is present
|
|
||||||
if (!attributes.containsKey(usernameAttribute)) {
|
|
||||||
attributes.put(usernameAttribute, attributes.getOrDefault("email", ""));
|
|
||||||
usernameAttribute = "email";
|
|
||||||
logger.info("Adjusted username attribute to use email");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a new OidcUser with adjusted attributes
|
|
||||||
return new DefaultOidcUser(
|
|
||||||
user.getAuthorities(),
|
|
||||||
userRequest.getIdToken(),
|
|
||||||
user.getUserInfo(),
|
|
||||||
usernameAttribute);
|
|
||||||
} catch (java.lang.IllegalArgumentException e) {
|
|
||||||
throw new OAuth2AuthenticationException(
|
|
||||||
new OAuth2Error(e.getMessage()), e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +1,13 @@
|
||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
@ -21,6 +24,11 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.GithubProvider;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.GoogleProvider;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.KeycloakProvider;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
||||||
import stirling.software.SPDF.model.Authority;
|
import stirling.software.SPDF.model.Authority;
|
||||||
import stirling.software.SPDF.model.Role;
|
import stirling.software.SPDF.model.Role;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
@ -31,6 +39,7 @@ import stirling.software.SPDF.repository.UserRepository;
|
||||||
public class AccountWebController {
|
public class AccountWebController {
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AccountWebController.class);
|
||||||
|
|
||||||
@GetMapping("/login")
|
@GetMapping("/login")
|
||||||
public String login(HttpServletRequest request, Model model, Authentication authentication) {
|
public String login(HttpServletRequest request, Model model, Authentication authentication) {
|
||||||
|
@ -38,6 +47,33 @@ public class AccountWebController {
|
||||||
return "redirect:/";
|
return "redirect:/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> providerList = new HashMap<>();
|
||||||
|
|
||||||
|
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
|
if (oauth != null) {
|
||||||
|
if (oauth.isSettingsValid()) {
|
||||||
|
providerList.put("oidc", "OpenID Connect");
|
||||||
|
}
|
||||||
|
Client client = oauth.getClient();
|
||||||
|
if (client != null) {
|
||||||
|
GoogleProvider google = client.getGoogle();
|
||||||
|
if (google.isSettingsValid()) {
|
||||||
|
providerList.put("google", "Google");
|
||||||
|
}
|
||||||
|
|
||||||
|
GithubProvider github = client.getGithub();
|
||||||
|
if (github.isSettingsValid()) {
|
||||||
|
providerList.put("github", "Github");
|
||||||
|
}
|
||||||
|
|
||||||
|
KeycloakProvider keycloak = client.getKeycloak();
|
||||||
|
if (keycloak.isSettingsValid()) {
|
||||||
|
providerList.put("keycloak", "Keycloak");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
model.addAttribute("providerlist", providerList);
|
||||||
|
|
||||||
model.addAttribute(
|
model.addAttribute(
|
||||||
"oAuth2Enabled", applicationProperties.getSecurity().getOAUTH2().getEnabled());
|
"oAuth2Enabled", applicationProperties.getSecurity().getOAUTH2().getEnabled());
|
||||||
|
|
||||||
|
@ -80,6 +116,19 @@ public class AccountWebController {
|
||||||
break;
|
break;
|
||||||
case "invalid_token_response":
|
case "invalid_token_response":
|
||||||
erroroauth = "login.oauth2InvalidTokenResponse";
|
erroroauth = "login.oauth2InvalidTokenResponse";
|
||||||
|
break;
|
||||||
|
case "authorization_request_not_found":
|
||||||
|
erroroauth = "login.oauth2RequestNotFound";
|
||||||
|
break;
|
||||||
|
case "access_denied":
|
||||||
|
erroroauth = "login.oauth2AccessDenied";
|
||||||
|
break;
|
||||||
|
case "invalid_user_info_response":
|
||||||
|
erroroauth = "login.oauth2InvalidUserInfoResponse";
|
||||||
|
break;
|
||||||
|
case "invalid_request":
|
||||||
|
erroroauth = "login.oauth2invalidRequest";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.PropertySource;
|
import org.springframework.context.annotation.PropertySource;
|
||||||
|
@ -23,6 +25,7 @@ public class ApplicationProperties {
|
||||||
private Metrics metrics;
|
private Metrics metrics;
|
||||||
private AutomaticallyGenerated automaticallyGenerated;
|
private AutomaticallyGenerated automaticallyGenerated;
|
||||||
private AutoPipeline autoPipeline;
|
private AutoPipeline autoPipeline;
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ApplicationProperties.class);
|
||||||
|
|
||||||
public AutoPipeline getAutoPipeline() {
|
public AutoPipeline getAutoPipeline() {
|
||||||
return autoPipeline != null ? autoPipeline : new AutoPipeline();
|
return autoPipeline != null ? autoPipeline : new AutoPipeline();
|
||||||
|
@ -182,13 +185,12 @@ public class ApplicationProperties {
|
||||||
+ oauth2
|
+ oauth2
|
||||||
+ ", initialLogin="
|
+ ", initialLogin="
|
||||||
+ initialLogin
|
+ initialLogin
|
||||||
+ ", csrfDisabled="
|
+ ", csrfDisabled="
|
||||||
+ csrfDisabled
|
+ csrfDisabled
|
||||||
+ "]";
|
+ "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InitialLogin {
|
public static class InitialLogin {
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
@ -219,22 +221,21 @@ public class ApplicationProperties {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class OAUTH2 {
|
public static class OAUTH2 {
|
||||||
|
private Boolean enabled = false;
|
||||||
private boolean enabled;
|
|
||||||
private String issuer;
|
private String issuer;
|
||||||
private String clientId;
|
private String clientId;
|
||||||
private String clientSecret;
|
private String clientSecret;
|
||||||
private boolean autoCreateUser;
|
private Boolean autoCreateUser = false;
|
||||||
private String useAsUsername;
|
private String useAsUsername;
|
||||||
|
private Collection<String> scopes = new ArrayList<>();
|
||||||
private String provider;
|
private String provider;
|
||||||
|
private Client client = new Client();
|
||||||
|
|
||||||
private Collection<String> scopes = new ArrayList<String>();
|
public Boolean getEnabled() {
|
||||||
|
|
||||||
public boolean getEnabled() {
|
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
public void setEnabled(Boolean enabled) {
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,19 +263,16 @@ public class ApplicationProperties {
|
||||||
this.clientSecret = clientSecret;
|
this.clientSecret = clientSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getAutoCreateUser() {
|
public Boolean getAutoCreateUser() {
|
||||||
return autoCreateUser;
|
return autoCreateUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAutoCreateUser(boolean autoCreateUser) {
|
public void setAutoCreateUser(Boolean autoCreateUser) {
|
||||||
this.autoCreateUser = autoCreateUser;
|
this.autoCreateUser = autoCreateUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUseAsUsername() {
|
public String getUseAsUsername() {
|
||||||
if (useAsUsername != null && useAsUsername.trim().length() > 0) {
|
return useAsUsername;
|
||||||
return useAsUsername;
|
|
||||||
}
|
|
||||||
return "email";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUseAsUsername(String useAsUsername) {
|
public void setUseAsUsername(String useAsUsername) {
|
||||||
|
@ -293,14 +291,44 @@ public class ApplicationProperties {
|
||||||
return scopes;
|
return scopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setScopes(String scpoes) {
|
public void setScopes(String scopes) {
|
||||||
List<String> scopesList =
|
List<String> scopesList =
|
||||||
Arrays.stream(scpoes.split(","))
|
Arrays.stream(scopes.split(","))
|
||||||
.map(String::trim)
|
.map(String::trim)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
this.scopes.addAll(scopesList);
|
this.scopes.addAll(scopesList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Client getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClient(Client client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSettingsValid() {
|
||||||
|
return isValid(this.getIssuer(), "issuer")
|
||||||
|
&& isValid(this.getClientId(), "clientId")
|
||||||
|
&& isValid(this.getClientSecret(), "clientSecret")
|
||||||
|
&& isValid(this.getScopes(), "scopes")
|
||||||
|
&& isValid(this.getUseAsUsername(), "useAsUsername");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "OAUTH2 [enabled="
|
return "OAUTH2 [enabled="
|
||||||
|
@ -315,12 +343,353 @@ public class ApplicationProperties {
|
||||||
+ autoCreateUser
|
+ autoCreateUser
|
||||||
+ ", useAsUsername="
|
+ ", useAsUsername="
|
||||||
+ useAsUsername
|
+ useAsUsername
|
||||||
+ ", provider"
|
+ ", provider="
|
||||||
+ provider
|
+ provider
|
||||||
+ ", scopes="
|
+ ", scopes="
|
||||||
+ scopes
|
+ scopes
|
||||||
+ "]";
|
+ "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Client {
|
||||||
|
private GoogleProvider google = new GoogleProvider();
|
||||||
|
private GithubProvider github = new GithubProvider();
|
||||||
|
private KeycloakProvider keycloak = new KeycloakProvider();
|
||||||
|
|
||||||
|
public Provider get(String registrationId) throws Exception {
|
||||||
|
switch (registrationId) {
|
||||||
|
case "gogole":
|
||||||
|
return getGoogle();
|
||||||
|
case "github":
|
||||||
|
return getGithub();
|
||||||
|
case "keycloak":
|
||||||
|
return getKeycloak();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new Exception("Provider not supported, use custom setting.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public GoogleProvider getGoogle() {
|
||||||
|
return google;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGoogle(GoogleProvider google) {
|
||||||
|
this.google = google;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GithubProvider getGithub() {
|
||||||
|
return github;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGithub(GithubProvider github) {
|
||||||
|
this.github = github;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeycloakProvider getKeycloak() {
|
||||||
|
return keycloak;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeycloak(KeycloakProvider keycloak) {
|
||||||
|
this.keycloak = keycloak;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Client [google="
|
||||||
|
+ google
|
||||||
|
+ ", github="
|
||||||
|
+ github
|
||||||
|
+ ", keycloak="
|
||||||
|
+ keycloak
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GoogleProvider extends Provider {
|
||||||
|
|
||||||
|
private static final String authorizationUri =
|
||||||
|
"https://accounts.google.com/o/oauth2/v2/auth";
|
||||||
|
private static final String tokenUri = "https://www.googleapis.com/oauth2/v4/token";
|
||||||
|
private static final String userInfoUri =
|
||||||
|
"https://www.googleapis.com/oauth2/v3/userinfo?alt=json";
|
||||||
|
|
||||||
|
public String getAuthorizationuri() {
|
||||||
|
return authorizationUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTokenuri() {
|
||||||
|
return tokenUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserinfouri() {
|
||||||
|
return userInfoUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String clientId;
|
||||||
|
private String clientSecret;
|
||||||
|
private Collection<String> scopes = new ArrayList<>();
|
||||||
|
private String useAsUsername = "email";
|
||||||
|
|
||||||
|
@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.add("https://www.googleapis.com/auth/userinfo.email");
|
||||||
|
scopes.add("https://www.googleapis.com/auth/userinfo.profile");
|
||||||
|
}
|
||||||
|
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 "Google [clientId="
|
||||||
|
+ clientId
|
||||||
|
+ ", clientSecret="
|
||||||
|
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
|
||||||
|
+ ", scopes="
|
||||||
|
+ scopes
|
||||||
|
+ ", useAsUsername="
|
||||||
|
+ useAsUsername
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static 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";
|
||||||
|
|
||||||
|
public String getAuthorizationuri() {
|
||||||
|
return authorizationUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTokenuri() {
|
||||||
|
return tokenUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserinfouri() {
|
||||||
|
return userInfoUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String clientId;
|
||||||
|
private String clientSecret;
|
||||||
|
private Collection<String> scopes = new ArrayList<>();
|
||||||
|
private String useAsUsername = "login";
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<String> getScopes() {
|
||||||
|
if (scopes == null || scopes.isEmpty()) {
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSettingsValid() {
|
||||||
|
return super.isValid(this.getClientId(), "clientId")
|
||||||
|
&& super.isValid(this.getClientSecret(), "clientSecret")
|
||||||
|
&& super.isValid(this.getScopes(), "scopes")
|
||||||
|
&& isValid(this.getUseAsUsername(), "useAsUsername");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class KeycloakProvider extends Provider {
|
||||||
|
private String issuer;
|
||||||
|
private String clientId;
|
||||||
|
private String clientSecret;
|
||||||
|
private Collection<String> scopes = new ArrayList<>();
|
||||||
|
private String useAsUsername = "email";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIssuer() {
|
||||||
|
return this.issuer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setIssuer(String issuer) {
|
||||||
|
this.issuer = 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.add("openid");
|
||||||
|
scopes.add("profile");
|
||||||
|
scopes.add("email");
|
||||||
|
}
|
||||||
|
return scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "Keycloak [issuer="
|
||||||
|
+ issuer
|
||||||
|
+ ", clientId="
|
||||||
|
+ clientId
|
||||||
|
+ ", clientSecret="
|
||||||
|
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
|
||||||
|
+ ", scopes="
|
||||||
|
+ scopes
|
||||||
|
+ ", useAsUsername="
|
||||||
|
+ useAsUsername
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
87
src/main/java/stirling/software/SPDF/model/Provider.java
Normal file
87
src/main/java/stirling/software/SPDF/model/Provider.java
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package stirling.software.SPDF.model;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class Provider implements ProviderInterface {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isValid(String value, String name) {
|
||||||
|
if (value != null && !value.trim().isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
// throw new IllegalArgumentException(getName() + ": " + name + " is required!");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isValid(Collection<String> value, String name) {
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
// throw new IllegalArgumentException(getName() + ": " + name + " is required!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getScopes() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'getScope'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setScopes(String scopes) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'setScope'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUseAsUsername() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'getUseAsUsername'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUseAsUsername(String useAsUsername) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'setUseAsUsername'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIssuer() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'getIssuer'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setIssuer(String issuer) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'setIssuer'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClientSecret() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'getClientSecret'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setClientSecret(String clientSecret) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'setClientSecret'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClientId() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'getClientId'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setClientId(String clientId) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'setClientId'");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
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);
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ public class RequestUriUtils {
|
||||||
public static boolean isStaticResource(String requestURI) {
|
public static boolean isStaticResource(String requestURI) {
|
||||||
|
|
||||||
return requestURI.startsWith("/css/")
|
return requestURI.startsWith("/css/")
|
||||||
|
|| requestURI.startsWith("/fonts/")
|
||||||
|| requestURI.startsWith("/js/")
|
|| requestURI.startsWith("/js/")
|
||||||
|| requestURI.startsWith("/images/")
|
|| requestURI.startsWith("/images/")
|
||||||
|| requestURI.startsWith("/public/")
|
|| requestURI.startsWith("/public/")
|
||||||
|
|
15
src/main/java/stirling/software/SPDF/utils/UrlUtils.java
Normal file
15
src/main/java/stirling/software/SPDF/utils/UrlUtils.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
public class UrlUtils {
|
||||||
|
|
||||||
|
public static String getOrigin(HttpServletRequest request) {
|
||||||
|
String scheme = request.getScheme(); // http or https
|
||||||
|
String serverName = request.getServerName(); // localhost
|
||||||
|
int serverPort = request.getServerPort(); // 8080
|
||||||
|
String contextPath = request.getContextPath(); // /myapp
|
||||||
|
|
||||||
|
return scheme + "://" + serverName + ":" + serverPort + contextPath;
|
||||||
|
}
|
||||||
|
}
|
|
@ -452,6 +452,11 @@ login.locked=Your account has been locked.
|
||||||
login.signinTitle=Please sign in
|
login.signinTitle=Please sign in
|
||||||
login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي
|
login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي
|
||||||
login.oauth2AutoCreateDisabled=تم تعطيل مستخدم الإنشاء التلقائي لـ OAuth2
|
login.oauth2AutoCreateDisabled=تم تعطيل مستخدم الإنشاء التلقائي لـ OAuth2
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=Вашият акаунт е заключен.
|
||||||
login.signinTitle=Моля впишете се
|
login.signinTitle=Моля впишете се
|
||||||
login.ssoSignIn=Влизане чрез еднократно влизане
|
login.ssoSignIn=Влизане чрез еднократно влизане
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано
|
login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=Compte bloquejat
|
||||||
login.signinTitle=Autenticat
|
login.signinTitle=Autenticat
|
||||||
login.ssoSignIn=Inicia sessió mitjançant l'inici de sessió ún
|
login.ssoSignIn=Inicia sessió mitjançant l'inici de sessió ún
|
||||||
login.oauth2AutoCreateDisabled=L'usuari de creació automàtica OAUTH2 està desactivat
|
login.oauth2AutoCreateDisabled=L'usuari de creació automàtica OAUTH2 està desactivat
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -59,8 +59,8 @@ deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht w
|
||||||
deleteUsernameExistsMessage=Der Benutzername existiert nicht und kann nicht gelöscht werden.
|
deleteUsernameExistsMessage=Der Benutzername existiert nicht und kann nicht gelöscht werden.
|
||||||
downgradeCurrentUserMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden
|
downgradeCurrentUserMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden
|
||||||
downgradeCurrentUserLongMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden. Daher wird der aktuelle Benutzer nicht angezeigt.
|
downgradeCurrentUserLongMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden. Daher wird der aktuelle Benutzer nicht angezeigt.
|
||||||
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
userAlreadyExistsOAuthMessage=Der Benutzer ist bereits als OAuth2-Benutzer vorhanden.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=Der Benutzer ist bereits als Webbenutzer vorhanden.
|
||||||
error=Fehler
|
error=Fehler
|
||||||
oops=Hoppla!
|
oops=Hoppla!
|
||||||
help=Hilfe
|
help=Hilfe
|
||||||
|
@ -71,7 +71,7 @@ visitGithub=GitHub-Repository besuchen
|
||||||
donate=Spenden
|
donate=Spenden
|
||||||
color=Farbe
|
color=Farbe
|
||||||
sponsor=Sponsor
|
sponsor=Sponsor
|
||||||
info=Info
|
info=Die Info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,18 +105,18 @@ pipelineOptions.validateButton=Validieren
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
#############
|
#############
|
||||||
navbar.favorite=Favorites
|
navbar.favorite=Favoriten
|
||||||
navbar.darkmode=Dunkler Modus
|
navbar.darkmode=Dunkler Modus
|
||||||
navbar.language=Languages
|
navbar.language=Sprachen
|
||||||
navbar.settings=Einstellungen
|
navbar.settings=Einstellungen
|
||||||
navbar.allTools=Tools
|
navbar.allTools=Werkzeuge
|
||||||
navbar.multiTool=Multi Tools
|
navbar.multiTool=Multitools
|
||||||
navbar.sections.organize=Organize
|
navbar.sections.organize=Organisieren
|
||||||
navbar.sections.convertTo=Convert to PDF
|
navbar.sections.convertTo=In PDF konvertieren
|
||||||
navbar.sections.convertFrom=Convert from PDF
|
navbar.sections.convertFrom=Konvertieren von PDF
|
||||||
navbar.sections.security=Sign & Security
|
navbar.sections.security=Zeichen und Sicherheit
|
||||||
navbar.sections.advance=Advanced
|
navbar.sections.advance=Fortschrittlich
|
||||||
navbar.sections.edit=View & Edit
|
navbar.sections.edit=Anzeigen und Bearbeiten
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# SETTINGS #
|
# SETTINGS #
|
||||||
|
@ -185,7 +185,7 @@ adminUserSettings.internalApiUser=Interner API-Benutzer
|
||||||
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
|
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
|
||||||
adminUserSettings.submit=Benutzer speichern
|
adminUserSettings.submit=Benutzer speichern
|
||||||
adminUserSettings.changeUserRole=Benutzerrolle ändern
|
adminUserSettings.changeUserRole=Benutzerrolle ändern
|
||||||
adminUserSettings.authenticated=Authenticated
|
adminUserSettings.authenticated=Authentifiziert
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Autorisierungsanfrage nicht gefunden
|
||||||
|
login.oauth2InvalidUserInfoResponse=Ungültige Benutzerinformationsantwort
|
||||||
|
login.oauth2invalidRequest=ungültige Anfrage
|
||||||
|
login.oauth2AccessDenied=Zugriff abgelehnt
|
||||||
|
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
@ -704,7 +709,7 @@ repair.submit=Reparieren
|
||||||
#flatten
|
#flatten
|
||||||
flatten.title=Abflachen
|
flatten.title=Abflachen
|
||||||
flatten.header=PDFs reduzieren
|
flatten.header=PDFs reduzieren
|
||||||
flatten.flattenOnlyForms=Flatten only forms
|
flatten.flattenOnlyForms=Nur Formulare abflachen
|
||||||
flatten.submit=Abflachen
|
flatten.submit=Abflachen
|
||||||
|
|
||||||
|
|
||||||
|
@ -752,7 +757,7 @@ extractImages.submit=Extrahieren
|
||||||
fileToPDF.title=Datei in PDF
|
fileToPDF.title=Datei in PDF
|
||||||
fileToPDF.header=Beliebige Dateien in PDF konvertieren
|
fileToPDF.header=Beliebige Dateien in PDF konvertieren
|
||||||
fileToPDF.credit=Dieser Dienst verwendet LibreOffice und Unoconv für die Dateikonvertierung.
|
fileToPDF.credit=Dieser Dienst verwendet LibreOffice und Unoconv für die Dateikonvertierung.
|
||||||
fileToPDF.supportedFileTypesInfo=Supported File types
|
fileToPDF.supportedFileTypesInfo=Unterstützte Dateitypen
|
||||||
fileToPDF.supportedFileTypes=Unterstützte Dateitypen sollten die folgenden enthalten, eine vollständige aktualisierte Liste der unterstützten Formate finden Sie jedoch in der LibreOffice-Dokumentation
|
fileToPDF.supportedFileTypes=Unterstützte Dateitypen sollten die folgenden enthalten, eine vollständige aktualisierte Liste der unterstützten Formate finden Sie jedoch in der LibreOffice-Dokumentation
|
||||||
fileToPDF.submit=In PDF konvertieren
|
fileToPDF.submit=In PDF konvertieren
|
||||||
|
|
||||||
|
@ -805,7 +810,7 @@ pdfOrganiser.placeholder=(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF-Multitool
|
multiTool.title=PDF-Multitool
|
||||||
multiTool.header=PDF-Multitool
|
multiTool.header=PDF-Multitool
|
||||||
multiTool.uploadPrompts=File Name
|
multiTool.uploadPrompts=Dateiname
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF anzeigen
|
viewPdf.title=PDF anzeigen
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=Ο λογαριασμός σας έχει κλειδωθεί.
|
||||||
login.signinTitle=Παρακαλώ, συνδεθείτε
|
login.signinTitle=Παρακαλώ, συνδεθείτε
|
||||||
login.ssoSignIn=Σύνδεση μέσω μοναδικής σύνδεσης
|
login.ssoSignIn=Σύνδεση μέσω μοναδικής σύνδεσης
|
||||||
login.oauth2AutoCreateDisabled=Απενεργοποιήθηκε ο χρήστης αυτόματης δημιουργίας OAUTH2
|
login.oauth2AutoCreateDisabled=Απενεργοποιήθηκε ο χρήστης αυτόματης δημιουργίας OAUTH2
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -114,7 +114,7 @@ navbar.multiTool=Multi Outils
|
||||||
navbar.sections.organize=Oragnisation
|
navbar.sections.organize=Oragnisation
|
||||||
navbar.sections.convertTo=Convertir en PDF
|
navbar.sections.convertTo=Convertir en PDF
|
||||||
navbar.sections.convertFrom=Convertir depuis PDF
|
navbar.sections.convertFrom=Convertir depuis PDF
|
||||||
navbar.sections.security=Signalisation et sécurité
|
navbar.sections.security=Signature et sécurité
|
||||||
navbar.sections.advance=Mode avancé
|
navbar.sections.advance=Mode avancé
|
||||||
navbar.sections.edit=Voir la modification
|
navbar.sections.edit=Voir la modification
|
||||||
|
|
||||||
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=आपका खाता लॉक कर दिया गया
|
||||||
login.signinTitle=कृपया साइन इन करें
|
login.signinTitle=कृपया साइन इन करें
|
||||||
login.ssoSignIn=सिंगल साइन - ऑन के ज़रिए लॉग इन करें
|
login.ssoSignIn=सिंगल साइन - ऑन के ज़रिए लॉग इन करें
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 ऑटो - क्रिएट यूज़र अक्षम किया गया
|
login.oauth2AutoCreateDisabled=OAUTH2 ऑटो - क्रिएट यूज़र अक्षम किया गया
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=A fiókja zárolva lett!
|
||||||
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 Felhasználó automatikus létrehozása letiltva
|
login.oauth2AutoCreateDisabled=OAUTH2 Felhasználó automatikus létrehozása letiltva
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Richiesta di autorizzazione non trovata
|
||||||
|
login.oauth2InvalidUserInfoResponse=Risposta relativa alle informazioni utente non valida
|
||||||
|
login.oauth2invalidRequest=Richiesta non valida
|
||||||
|
login.oauth2AccessDenied=Accesso negato
|
||||||
|
login.oauth2InvalidTokenResponse=Risposta token non valida
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=あなたのアカウントはロックされています。
|
||||||
login.signinTitle=サインインしてください
|
login.signinTitle=サインインしてください
|
||||||
login.ssoSignIn=シングルサインオンでログイン
|
login.ssoSignIn=シングルサインオンでログイン
|
||||||
login.oauth2AutoCreateDisabled=OAuth 2自動作成ユーザーが無効
|
login.oauth2AutoCreateDisabled=OAuth 2自動作成ユーザーが無効
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=계정이 잠겼습니다.
|
||||||
login.signinTitle=로그인해 주세요.
|
login.signinTitle=로그인해 주세요.
|
||||||
login.ssoSignIn=싱글사인온을 통한 로그인
|
login.ssoSignIn=싱글사인온을 통한 로그인
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 사용자 자동 생성 비활성화됨
|
login.oauth2AutoCreateDisabled=OAUTH2 사용자 자동 생성 비활성화됨
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=Your account has been locked.
|
||||||
login.signinTitle=Please sign in
|
login.signinTitle=Please sign in
|
||||||
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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=Your account has been locked.
|
||||||
login.signinTitle=Please sign in
|
login.signinTitle=Please sign in
|
||||||
login.ssoSignIn=Iniciar sessão através de início de sessão único
|
login.ssoSignIn=Iniciar sessão através de início de sessão único
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Criar Usuário Desativado
|
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Criar Usuário Desativado
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=A sua conta foi bloqueada.
|
||||||
login.signinTitle=Introduza os seus dados de acesso
|
login.signinTitle=Introduza os seus dados de acesso
|
||||||
login.ssoSignIn=Iniciar sessão através de início de sessão único
|
login.ssoSignIn=Iniciar sessão através de início de sessão único
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Criação Automática de Utilizador Desativada
|
login.oauth2AutoCreateDisabled=OAUTH2 Criação Automática de Utilizador Desativada
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=Your account has been locked.
|
||||||
login.signinTitle=Please sign in
|
login.signinTitle=Please sign in
|
||||||
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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=Ваша учетная запись заблокирована.
|
||||||
login.signinTitle=Пожалуйста, войдите
|
login.signinTitle=Пожалуйста, войдите
|
||||||
login.ssoSignIn=Вход через единый вход
|
login.ssoSignIn=Вход через единый вход
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Автоматическое создание пользователя отключено
|
login.oauth2AutoCreateDisabled=OAUTH2 Автоматическое создание пользователя отключено
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=Your account has been locked.
|
||||||
login.signinTitle=Please sign in
|
login.signinTitle=Please sign in
|
||||||
login.ssoSignIn=Logga in via enkel inloggning
|
login.ssoSignIn=Logga in via enkel inloggning
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User inaktiverad
|
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User inaktiverad
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ 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.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=Ваш обліковий запис заблоковано.
|
||||||
login.signinTitle=Будь ласка, увійдіть
|
login.signinTitle=Будь ласка, увійдіть
|
||||||
login.ssoSignIn=Увійти через єдиний вхід
|
login.ssoSignIn=Увійти через єдиний вхід
|
||||||
login.oauth2AutoCreateDisabled=Автоматичне створення користувача OAUTH2 ВИМКНЕНО
|
login.oauth2AutoCreateDisabled=Автоматичне створення користувача OAUTH2 ВИМКНЕНО
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -105,18 +105,18 @@ pipelineOptions.validateButton=验证
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
#############
|
#############
|
||||||
navbar.favorite=Favorites
|
navbar.favorite=收藏
|
||||||
navbar.darkmode=暗模式
|
navbar.darkmode=暗黑模式
|
||||||
navbar.language=Languages
|
navbar.language=语言
|
||||||
navbar.settings=设置
|
navbar.settings=设置
|
||||||
navbar.allTools=Tools
|
navbar.allTools=工具箱
|
||||||
navbar.multiTool=Multi Tools
|
navbar.multiTool=多功能工具
|
||||||
navbar.sections.organize=Organize
|
navbar.sections.organize=组织
|
||||||
navbar.sections.convertTo=Convert to PDF
|
navbar.sections.convertTo=转换成PDF
|
||||||
navbar.sections.convertFrom=Convert from PDF
|
navbar.sections.convertFrom=从PDF转换
|
||||||
navbar.sections.security=Sign & Security
|
navbar.sections.security=签名和安全
|
||||||
navbar.sections.advance=Advanced
|
navbar.sections.advance=高级功能
|
||||||
navbar.sections.edit=View & Edit
|
navbar.sections.edit=查看和编辑
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# SETTINGS #
|
# SETTINGS #
|
||||||
|
@ -452,6 +452,11 @@ login.locked=您的账户已被锁定。
|
||||||
login.signinTitle=请登录
|
login.signinTitle=请登录
|
||||||
login.ssoSignIn=通过单点登录登录
|
login.ssoSignIn=通过单点登录登录
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2自动创建用户已禁用
|
login.oauth2AutoCreateDisabled=OAUTH2自动创建用户已禁用
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
@ -805,7 +810,7 @@ pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1)
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF多功能工具
|
multiTool.title=PDF多功能工具
|
||||||
multiTool.header=PDF多功能工具
|
multiTool.header=PDF多功能工具
|
||||||
multiTool.uploadPrompts=File Name
|
multiTool.uploadPrompts=文件名
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=浏览PDF
|
viewPdf.title=浏览PDF
|
||||||
|
|
|
@ -452,6 +452,11 @@ login.locked=您的帳戶已被鎖定。
|
||||||
login.signinTitle=請登入
|
login.signinTitle=請登入
|
||||||
login.ssoSignIn=透過織網單一簽入
|
login.ssoSignIn=透過織網單一簽入
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2自動建立使用者已停用
|
login.oauth2AutoCreateDisabled=OAUTH2自動建立使用者已停用
|
||||||
|
login.oauth2RequestNotFound=Authorization request not found
|
||||||
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
|
login.oauth2invalidRequest=Invalid Request
|
||||||
|
login.oauth2AccessDenied=Access Denied
|
||||||
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
|
|
|
@ -19,6 +19,23 @@ security:
|
||||||
# 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'
|
||||||
|
# client:
|
||||||
|
# google:
|
||||||
|
# clientId: "" # Client ID 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
|
||||||
|
# useAsUsername: "email" # Field to use as the username for Google OAuth2
|
||||||
|
# github:
|
||||||
|
# clientId: "" # Client ID for GitHub OAuth2
|
||||||
|
# clientSecret: "" # Client Secret for GitHub OAuth2
|
||||||
|
# scopes: "read:user" # Scope for GitHub OAuth2
|
||||||
|
# useAsUsername: "login" # Field to use as the username for GitHub OAuth2
|
||||||
|
# keycloak:
|
||||||
|
# issuer: "http://192.168.0.123:8888/realms/stirling-pdf" # URL of the Keycloak realm's OpenID Connect Discovery endpoint
|
||||||
|
# clientId: "stirling-pdf" # Client ID for Keycloak OAuth2
|
||||||
|
# clientSecret: "" # Client Secret for Keycloak OAuth2
|
||||||
|
# scopes: "openid, profile, email" # Scopes for Keycloak OAuth2
|
||||||
|
# useAsUsername: "email" # Field to use as the username for Keycloak OAuth2
|
||||||
|
|
||||||
system:
|
system:
|
||||||
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
|
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
|
||||||
|
|
|
@ -60,22 +60,22 @@ function handleDropdownItemClick(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
|
||||||
document.querySelectorAll(".col-lg-2.col-sm-6").forEach((element) => {
|
document.querySelectorAll(".col-lg-2.col-sm-6").forEach((element) => {
|
||||||
const dropdownItems = element.querySelectorAll(".dropdown-item");
|
const dropdownItems = element.querySelectorAll(".dropdown-item");
|
||||||
const items = Array.from(dropdownItems).filter(item => !item.querySelector("hr.dropdown-divider"));
|
const items = Array.from(dropdownItems).filter(item => !item.querySelector("hr.dropdown-divider"));
|
||||||
|
|
||||||
if (items.length <= 2) {
|
if (items.length <= 2) {
|
||||||
if (
|
if (
|
||||||
element.previousElementSibling &&
|
element.previousElementSibling &&
|
||||||
element.previousElementSibling.classList.contains("col-lg-2") &&
|
element.previousElementSibling.classList.contains("col-lg-2") &&
|
||||||
element.previousElementSibling.classList.contains("nav-item-separator")
|
element.previousElementSibling.classList.contains("nav-item-separator")
|
||||||
) {
|
) {
|
||||||
element.previousElementSibling.remove();
|
element.previousElementSibling.remove();
|
||||||
}
|
}
|
||||||
element.remove();
|
element.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Sort languages by alphabet
|
//Sort languages by alphabet
|
||||||
const list = Array.from(document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').children).filter(
|
const list = Array.from(document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').children).filter(
|
||||||
|
|
|
@ -40,7 +40,6 @@ document.querySelector("#navbarSearchInput").addEventListener("input", function
|
||||||
var itemHref = item.getAttribute("href");
|
var itemHref = item.getAttribute("href");
|
||||||
var tags = item.getAttribute("data-bs-tags") || ""; // If no tags, default to empty string
|
var tags = item.getAttribute("data-bs-tags") || ""; // If no tags, default to empty string
|
||||||
|
|
||||||
|
|
||||||
if (titleElement && iconElement && itemHref !== "#") {
|
if (titleElement && iconElement && itemHref !== "#") {
|
||||||
var title = titleElement.innerText;
|
var title = titleElement.innerText;
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -20,13 +20,13 @@
|
||||||
<p th:text="#{pdfToPDFA.tip}"></p>
|
<p th:text="#{pdfToPDFA.tip}"></p>
|
||||||
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/pdf/pdfa}">
|
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/pdf/pdfa}">
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label th:text="#{pdfToPDFA.outputFormat}"></label>
|
<label th:text="#{pdfToPDFA.outputFormat}"></label>
|
||||||
<select class="form-control" name="outputFormat">
|
<select class="form-control" name="outputFormat">
|
||||||
<option value="pdfa-1">PDF/A-1b</option>
|
<option value="pdfa-1">PDF/A-1b</option>
|
||||||
<option value="pdfa">PDF/A-2b</option>
|
<option value="pdfa">PDF/A-2b</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToPDFA.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToPDFA.submit}"></button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
<button type="button" class="btn btn-secondary" onclick="copytrace()" th:text="#{error.copyStack}"></button>
|
<button type="button" class="btn btn-secondary" onclick="copytrace()" th:text="#{error.copyStack}"></button>
|
||||||
<button type="button" class="btn btn-info" onclick="showHelp()" th:text="#{help}"></button>
|
<button type="button" class="btn btn-info" onclick="showHelp()" th:text="#{help}"></button>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close" onclick="dismissError()">
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close" onclick="dismissError()">
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
close
|
close
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<!-- Stack trace section -->
|
<!-- Stack trace section -->
|
||||||
<div id="trace" style="max-height: 0; overflow: hidden;">
|
<div id="trace" style="max-height: 0; overflow: hidden;">
|
||||||
<div>
|
<div>
|
||||||
|
@ -23,11 +23,11 @@
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="helpModalLabel" th:text="#{help}"></h5>
|
<h5 class="modal-title" id="helpModalLabel" th:text="#{help}"></h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close" onclick="dismissError()">
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close" onclick="dismissError()">
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
close
|
close
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
|
@ -233,7 +233,6 @@
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#" th:href="@{multi-tool}"
|
<a class="nav-link" href="#" th:href="@{multi-tool}"
|
||||||
th:classappend="${currentPage}=='multi-tool' ? 'active' : ''" th:title="#{home.multiTool.desc}">
|
th:classappend="${currentPage}=='multi-tool' ? 'active' : ''" th:title="#{home.multiTool.desc}">
|
||||||
|
@ -254,7 +253,6 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#" title="#{home.compressPdfs.title}" th:href="@{compress-pdf}"
|
<a class="nav-link" href="#" title="#{home.compressPdfs.title}" th:href="@{compress-pdf}"
|
||||||
th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''" th:title="#{home.compressPdfs.desc}">
|
th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''" th:title="#{home.compressPdfs.desc}">
|
||||||
|
@ -276,8 +274,7 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<!--
|
<!-- <li class="nav-item">
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#" th:href="@{merge-pdfs}"
|
<a class="nav-link" href="#" th:href="@{merge-pdfs}"
|
||||||
th:classappend="${currentPage}=='merge-pdfs' ? 'active' : ''" th:title="#{home.merge.desc}">
|
th:classappend="${currentPage}=='merge-pdfs' ? 'active' : ''" th:title="#{home.merge.desc}">
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
|
@ -285,8 +282,8 @@
|
||||||
</span>
|
</span>
|
||||||
<span class="icon-text" th:data-text="#{home.merge.title}" th:text="#{home.merge.title}"></span>
|
<span class="icon-text" th:data-text="#{home.merge.title}" th:text="#{home.merge.title}"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li> -->
|
||||||
-->
|
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="navbar-nav flex-nowrap">
|
<ul class="navbar-nav flex-nowrap">
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
|
@ -329,24 +326,22 @@
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link" href="#" id="searchDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<a class="nav-link" href="#" id="searchDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
search
|
search
|
||||||
</span>
|
</span>
|
||||||
<span class="icon-text icon-hide">Search</span>
|
<span class="icon-text icon-hide">Search</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="searchDropdown">
|
||||||
<div class="dropdown-menu-wrapper px-xl-2 px-2">
|
<div class="dropdown-menu-wrapper px-xl-2 px-2">
|
||||||
<form class="d-flex p-2 search-form" id="searchForm">
|
<form class="d-flex p-2 search-form" id="searchForm">
|
||||||
<input class="form-control search-input" type="search" placeholder="Search" aria-label="Search" id="navbarSearchInput">
|
<input class="form-control search-input" type="search" placeholder="Search" aria-label="Search" id="navbarSearchInput">
|
||||||
</form>
|
</form>
|
||||||
<!-- Search Results -->
|
<!-- Search Results -->
|
||||||
<div id="searchResults" class="search-results scrollable-y dropdown-mw-20"></div>
|
<div id="searchResults" class="search-results scrollable-y dropdown-mw-20"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<!-- Settings Button -->
|
<!-- Settings Button -->
|
||||||
|
@ -405,9 +400,6 @@
|
||||||
target="_blank"></a>
|
target="_blank"></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="downloadOption" th:utext="#{settings.downloadOption.title}"></label>
|
<label for="downloadOption" th:utext="#{settings.downloadOption.title}"></label>
|
||||||
<select class="form-control" id="downloadOption">
|
<select class="form-control" id="downloadOption">
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
document.addEventListener('modeChanged', function(e) {
|
document.addEventListener('modeChanged', function(e) {
|
||||||
var mode = e.detail;
|
var mode = e.detail;
|
||||||
|
|
||||||
setInputMode("username", mode);
|
|
||||||
setInputMode("password", mode);
|
|
||||||
document.body.classList.remove("light-mode", "dark-mode", "rainbow-mode"); // remove all mode classes first
|
document.body.classList.remove("light-mode", "dark-mode", "rainbow-mode"); // remove all mode classes first
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
@ -117,7 +115,7 @@
|
||||||
|
|
||||||
<h1 class="h1 mb-3 fw-normal" th:text="${@appName}">Stirling-PDF</h1>
|
<h1 class="h1 mb-3 fw-normal" th:text="${@appName}">Stirling-PDF</h1>
|
||||||
<div th:if="${oAuth2Enabled}">
|
<div th:if="${oAuth2Enabled}">
|
||||||
<a href="oauth2/authorization/oidc" class="w-100 btn btn-lg btn-primary" th:text="#{login.ssoSignIn}">Login Via SSO</a>
|
<a href="#" class="w-100 btn btn-lg btn-primary" data-bs-toggle="modal" data-bs-target="#loginsModal" th:text="#{login.ssoSignIn}">Login Via SSO</a>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<div th:if="${erroroauth}" class="alert alert-danger text-center">
|
<div th:if="${erroroauth}" class="alert alert-danger text-center">
|
||||||
|
@ -126,7 +124,7 @@
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div th:if="${error}" class="alert alert-danger text-danger text-center">
|
<div th:if="${error}" class="alert alert-danger text-center">
|
||||||
<div th:if="${error}" th:text="#{${error}}">OAuth2: Error Message</div>
|
<div th:if="${error}" th:text="#{${error}}">OAuth2: Error Message</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-danger text-center">
|
<div class="text-danger text-center">
|
||||||
|
@ -170,5 +168,27 @@
|
||||||
</main>
|
</main>
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
|
<div th:if="${oAuth2Enabled}" class="modal fade" id="loginsModal" tabindex="-1" role="dialog" aria-labelledby="loginsModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="loginsModalLabel" th:text="#{login.ssoSignIn}"></h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3" th:each="provider : ${providerlist}">
|
||||||
|
<a th:href="@{|/oauth2/authorization/${provider.key}|}" th:text="${provider.value}" class="w-100 btn btn-lg btn-primary">OpenID Connect</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -25,7 +25,7 @@
|
||||||
<input type="checkbox" id="flattenOnlyForms" name="flattenOnlyForms">
|
<input type="checkbox" id="flattenOnlyForms" name="flattenOnlyForms">
|
||||||
<label for="flattenOnlyForms" th:text="#{flatten.flattenOnlyForms}" ></label>
|
<label for="flattenOnlyForms" th:text="#{flatten.flattenOnlyForms}" ></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{flatten.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{flatten.submit}"></button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 th:text="#{printFile.selectText.1}">Select Printer</h4> <!-- Assuming the message code printFile.selectText.3 corresponds to "Select Printer" -->
|
<h4 th:text="#{printFile.selectText.1}">Select Printer</h4> <!-- Assuming the message code printFile.selectText.3 corresponds to "Select Printer" -->
|
||||||
<label for="printerName" th:text="#{printFile.selectText.2}">Printer Name:</label> <!-- Assuming the message code printFile.selectText.4 corresponds to "Printer Name:" -->
|
<label for="printerName" th:text="#{printFile.selectText.2}">Printer Name:</label> <!-- Assuming the message code printFile.selectText.4 corresponds to "Printer Name:" -->
|
||||||
<input type="text" name="printerName" id="printerName" class="form-control">
|
<input type="text" name="printerName" id="printerName" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{printFile.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{printFile.submit}"></button>
|
||||||
|
|
Loading…
Reference in a new issue