Merge pull request #1284 from Ludy87/add_multi_oauth2

add multi OAuth2 Provider
This commit is contained in:
Anthony Stirling 2024-05-25 17:41:31 +01:00 committed by GitHub
commit f06d755899
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 1551 additions and 717 deletions

View file

@ -8,11 +8,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
@ -62,7 +58,7 @@ public class ConfigInitializer
Files.exists(userPath) ? Files.readAllLines(userPath) : new ArrayList<>();
List<String> resultLines = new ArrayList<>();
int position = 0;
for (String templateLine : templateLines) {
// Check if the line is a comment
if (templateLine.trim().startsWith("#")) {
@ -70,7 +66,7 @@ public class ConfigInitializer
if (!entry.isEmpty()) {
// Check if this comment has been uncommented in userLines
String key = entry.split(":")[0].trim();
addLine(resultLines, userLines, templateLine, key);
addLine(resultLines, userLines, templateLine, key, position);
} else {
resultLines.add(templateLine);
}
@ -78,15 +74,16 @@ public class ConfigInitializer
// Check if the line is a key-value pair
else if (templateLine.contains(":")) {
String key = templateLine.split(":")[0].trim();
addLine(resultLines, userLines, templateLine, key);
addLine(resultLines, userLines, templateLine, key, position);
}
// Handle empty lines
else if (templateLine.trim().length() == 0) {
resultLines.add("");
}
position++;
}
// Write the result to the user settings file
// Write the result to the user settings file
Files.write(userPath, resultLines);
}
@ -95,15 +92,19 @@ public class ConfigInitializer
Files.createFile(customSettingsPath);
}
}
//TODO check parent value instead of just indent lines for duplicate keys (like enabled etc)
private static void addLine(List<String> resultLines, List<String> userLines, String templateLine, String key) {
// TODO check parent value instead of just indent lines for duplicate keys (like enabled etc)
private static void addLine(
List<String> resultLines,
List<String> userLines,
String templateLine,
String key,
int position) {
boolean added = false;
int templateIndentationLevel = getIndentationLevel(templateLine);
int pos = 0;
for (String settingsLine : userLines) {
if (settingsLine.trim().startsWith(key + ":")) {
if (settingsLine.trim().startsWith(key + ":") && position == pos) {
int settingsIndentationLevel = getIndentationLevel(settingsLine);
// Check if it is correct settingsLine and has the same parent as templateLine
if (settingsIndentationLevel == templateIndentationLevel) {
@ -112,17 +113,18 @@ public class ConfigInitializer
break;
}
}
pos++;
}
if (!added) {
resultLines.add(templateLine);
}
}
}
private static int getIndentationLevel(String line) {
int indentationLevel = 0;
String trimmedLine = line.trim();
if (trimmedLine.startsWith("#")) {
line = trimmedLine.substring(1);
line = trimmedLine.substring(1);
}
for (char c : line.toCharArray()) {
if (c == ' ') {

View file

@ -2,6 +2,8 @@ package stirling.software.SPDF.config.security;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.CustomOAuth2UserService;
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.User;
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
@ -47,6 +53,8 @@ public class SecurityConfiguration {
@Autowired private CustomUserDetailsService userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
@ -140,6 +148,7 @@ public class SecurityConfiguration {
|| trimmedUri.startsWith("/images/")
|| trimmedUri.startsWith("/public/")
|| trimmedUri.startsWith("/css/")
|| trimmedUri.startsWith("/fonts/")
|| trimmedUri.startsWith("/js/")
|| trimmedUri.startsWith(
"/api/v1/info/status");
@ -150,7 +159,8 @@ public class SecurityConfiguration {
.authenticationProvider(authenticationProvider());
// Handle OAUTH2 Logins
if (applicationProperties.getSecurity().getOAUTH2().getEnabled()) {
if (applicationProperties.getSecurity().getOAUTH2() != null
&& applicationProperties.getSecurity().getOAUTH2().getEnabled()) {
http.oauth2Login(
oauth2 ->
@ -181,9 +191,10 @@ public class SecurityConfiguration {
.logout(
logout ->
logout.logoutSuccessHandler(
new CustomOAuth2LogoutSuccessHandler(
this.applicationProperties,
sessionRegistry())));
new CustomOAuth2LogoutSuccessHandler(
this.applicationProperties,
sessionRegistry()))
.invalidateHttpSession(true));
}
} else {
http.csrf(csrf -> csrf.disable())
@ -200,19 +211,127 @@ public class SecurityConfiguration {
havingValue = "true",
matchIfMissing = false)
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();
return ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
.registrationId("oidc")
.clientId(oauth.getClientId())
.clientSecret(oauth.getClientSecret())
.scope(oauth.getScopes())
.userNameAttributeName(oauth.getUseAsUsername())
.clientName("OIDC")
.build();
if (oauth == null || !oauth.getEnabled()) {
return Optional.empty();
}
Client client = oauth.getClient();
if (client == null) {
return Optional.empty();
}
GoogleProvider google = client.getGoogle();
return google != null && google.isSettingsValid()
? Optional.of(
ClientRegistration.withRegistrationId("google")
.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());
}
/*

View file

@ -101,6 +101,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
contextPath + "/images/",
contextPath + "/public/",
contextPath + "/css/",
contextPath + "/fonts/",
contextPath + "/js/",
contextPath + "/pdfjs/",
contextPath + "/api/v1/info/status",

View file

@ -41,6 +41,7 @@ public class CustomOAuth2AuthenticationFailureHandler
} else if (exception instanceof LockedException) {
logger.error("Account locked: ", exception);
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
return;
} else {
logger.error("Unhandled authentication exception", exception);
super.onAuthenticationFailure(request, response, exception);

View file

@ -6,6 +6,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import jakarta.servlet.ServletException;
@ -14,6 +15,8 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.model.ApplicationProperties;
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 {
@ -33,11 +36,30 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
public void onLogoutSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String param = "logout=true";
String registrationId = null;
String issuer = null;
String clientId = null;
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) {
param = "erroroauth=oauth2AuthenticationErrorWeb";
@ -49,36 +71,46 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
param = "error=oauth2AutoCreateDisabled";
}
String redirect_url = UrlUtils.getOrigin(request) + "/login?" + param;
HttpSession session = request.getSession(false);
if (session != null) {
String sessionId = session.getId();
sessionRegistry.removeSessionInformation(sessionId);
session.invalidate();
logger.debug("Session invalidated: " + sessionId);
logger.info("Session invalidated: " + sessionId);
}
switch (provider) {
switch (registrationId) {
case "keycloak":
// Add Keycloak specific logout URL if needed
String logoutUrl =
oauth.getIssuer()
issuer
+ "/protocol/openid-connect/logout"
+ "?client_id="
+ oauth.getClientId()
+ clientId
+ "&post_logout_redirect_uri="
+ response.encodeRedirectURL(
request.getScheme()
+ "://"
+ request.getHeader("host")
+ "/login?"
+ param);
logger.debug("Redirecting to Keycloak logout URL: " + logoutUrl);
+ response.encodeRedirectURL(redirect_url);
logger.info("Redirecting to Keycloak logout URL: " + logoutUrl);
response.sendRedirect(logoutUrl);
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":
// 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:
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);
break;
}

View file

@ -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);
}
}
}

View file

@ -1,10 +1,13 @@
package stirling.software.SPDF.controller.web;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
@ -21,6 +24,11 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
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.Role;
import stirling.software.SPDF.model.User;
@ -31,6 +39,7 @@ import stirling.software.SPDF.repository.UserRepository;
public class AccountWebController {
@Autowired ApplicationProperties applicationProperties;
private static final Logger logger = LoggerFactory.getLogger(AccountWebController.class);
@GetMapping("/login")
public String login(HttpServletRequest request, Model model, Authentication authentication) {
@ -38,6 +47,33 @@ public class AccountWebController {
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(
"oAuth2Enabled", applicationProperties.getSecurity().getOAUTH2().getEnabled());
@ -80,6 +116,19 @@ public class AccountWebController {
break;
case "invalid_token_response":
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:
break;
}

View file

@ -6,6 +6,8 @@ import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@ -23,6 +25,7 @@ public class ApplicationProperties {
private Metrics metrics;
private AutomaticallyGenerated automaticallyGenerated;
private AutoPipeline autoPipeline;
private static final Logger logger = LoggerFactory.getLogger(ApplicationProperties.class);
public AutoPipeline getAutoPipeline() {
return autoPipeline != null ? autoPipeline : new AutoPipeline();
@ -182,13 +185,12 @@ public class ApplicationProperties {
+ oauth2
+ ", initialLogin="
+ initialLogin
+ ", csrfDisabled="
+ ", csrfDisabled="
+ csrfDisabled
+ "]";
}
public static class InitialLogin {
private String username;
private String password;
@ -219,22 +221,21 @@ public class ApplicationProperties {
}
public static class OAUTH2 {
private boolean enabled;
private Boolean enabled = false;
private String issuer;
private String clientId;
private String clientSecret;
private boolean autoCreateUser;
private Boolean autoCreateUser = false;
private String useAsUsername;
private Collection<String> scopes = new ArrayList<>();
private String provider;
private Client client = new Client();
private Collection<String> scopes = new ArrayList<String>();
public boolean getEnabled() {
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
@ -262,19 +263,16 @@ public class ApplicationProperties {
this.clientSecret = clientSecret;
}
public boolean getAutoCreateUser() {
public Boolean getAutoCreateUser() {
return autoCreateUser;
}
public void setAutoCreateUser(boolean autoCreateUser) {
public void setAutoCreateUser(Boolean autoCreateUser) {
this.autoCreateUser = autoCreateUser;
}
public String getUseAsUsername() {
if (useAsUsername != null && useAsUsername.trim().length() > 0) {
return useAsUsername;
}
return "email";
return useAsUsername;
}
public void setUseAsUsername(String useAsUsername) {
@ -293,14 +291,44 @@ public class ApplicationProperties {
return scopes;
}
public void setScopes(String scpoes) {
public void setScopes(String scopes) {
List<String> scopesList =
Arrays.stream(scpoes.split(","))
Arrays.stream(scopes.split(","))
.map(String::trim)
.collect(Collectors.toList());
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
public String toString() {
return "OAUTH2 [enabled="
@ -315,12 +343,353 @@ public class ApplicationProperties {
+ autoCreateUser
+ ", useAsUsername="
+ useAsUsername
+ ", provider"
+ ", provider="
+ provider
+ ", 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");
}
}

View 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'");
}
}

View file

@ -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);
}

View file

@ -5,6 +5,7 @@ public class RequestUriUtils {
public static boolean isStaticResource(String requestURI) {
return requestURI.startsWith("/css/")
|| requestURI.startsWith("/fonts/")
|| requestURI.startsWith("/js/")
|| requestURI.startsWith("/images/")
|| requestURI.startsWith("/public/")

View 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;
}
}

View file

@ -452,6 +452,11 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي
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

View file

@ -452,6 +452,11 @@ login.locked=Вашият акаунт е заключен.
login.signinTitle=Моля впишете се
login.ssoSignIn=Влизане чрез еднократно влизане
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

View file

@ -452,6 +452,11 @@ login.locked=Compte bloquejat
login.signinTitle=Autenticat
login.ssoSignIn=Inicia sessió mitjançant l'inici de sessió ún
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

File diff suppressed because it is too large Load diff

View file

@ -452,6 +452,12 @@ login.locked=Ihr Konto wurde gesperrt.
login.signinTitle=Bitte melden Sie sich an.
login.ssoSignIn=Anmeldung per Single Sign-On
login.oauth2AutoCreateDisabled=OAUTH2 Benutzer automatisch erstellen deaktiviert
login.oauth2RequestNotFound=Authorization request not found
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2RequestNotFound=Authorization Request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
#auto-redact

View file

@ -452,6 +452,11 @@ login.locked=Ο λογαριασμός σας έχει κλειδωθεί.
login.signinTitle=Παρακαλώ, συνδεθείτε
login.ssoSignIn=Σύνδεση μέσω μοναδικής σύνδεσης
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

View file

@ -452,6 +452,12 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Login via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
login.oauth2RequestNotFound=Authorization request not found
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2RequestNotFound=Authorization Request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
#auto-redact

View file

@ -452,6 +452,11 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Login via Single Sign-on
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

View file

@ -452,6 +452,11 @@ login.locked=Su cuenta se ha bloqueado.
login.signinTitle=Por favor, inicie sesión
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.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

View file

@ -452,6 +452,11 @@ login.locked=Zure kontua blokeatu egin da.
login.signinTitle=Mesedez, hasi saioa
login.ssoSignIn=Hasi saioa Saioa hasteko modu bakarraren bidez
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

View file

@ -452,6 +452,11 @@ login.locked=Votre compte a été verrouillé.
login.signinTitle=Veuillez vous connecter
login.ssoSignIn=Se connecter via l'authentification unique
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

View file

@ -452,6 +452,11 @@ login.locked=आपका खाता लॉक कर दिया गया
login.signinTitle=कृपया साइन इन करें
login.ssoSignIn=सिंगल साइन - ऑन के ज़रिए लॉग इन करें
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

View file

@ -452,6 +452,11 @@ login.locked=A fiókja zárolva lett!
login.signinTitle=Kérjük, jelentkezzen be!
login.ssoSignIn=Bejelentkezés egyszeri bejelentkezéssel
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

View file

@ -452,6 +452,11 @@ login.locked=Akun Anda telah dikunci.
login.signinTitle=Silakan masuk
login.ssoSignIn=Masuk melalui Single Sign - on
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

View file

@ -452,6 +452,11 @@ login.locked=Il tuo account è stato bloccato.
login.signinTitle=Per favore accedi
login.ssoSignIn=Accedi tramite Single Sign-on
login.oauth2AutoCreateDisabled=Creazione automatica utente OAUTH2 DISABILITATA
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

View file

@ -452,6 +452,11 @@ login.locked=あなたのアカウントはロックされています。
login.signinTitle=サインインしてください
login.ssoSignIn=シングルサインオンでログイン
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

View file

@ -452,6 +452,11 @@ login.locked=계정이 잠겼습니다.
login.signinTitle=로그인해 주세요.
login.ssoSignIn=싱글사인온을 통한 로그인
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

View file

@ -452,6 +452,11 @@ login.locked=Je account is geblokkeerd.
login.signinTitle=Gelieve in te loggen
login.ssoSignIn=Inloggen via Single Sign-on
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

View file

@ -452,6 +452,11 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Zaloguj się za pomocą logowania jednokrotnego
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

View file

@ -452,6 +452,11 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Iniciar sessão através de início de sessão único
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

View file

@ -452,6 +452,11 @@ login.locked=A sua conta foi bloqueada.
login.signinTitle=Introduza os seus dados de acesso
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.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

View file

@ -452,6 +452,11 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Conectare prin conectare unică
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

View file

@ -452,6 +452,11 @@ login.locked=Ваша учетная запись заблокирована.
login.signinTitle=Пожалуйста, войдите
login.ssoSignIn=Вход через единый вход
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

View file

@ -452,6 +452,11 @@ login.locked=Váš účet bol uzamknutý.
login.signinTitle=Prosím, prihláste sa
login.ssoSignIn=Prihlásiť sa cez Single Sign-on
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

View file

@ -452,6 +452,11 @@ login.locked=Vaš nalog je zaključan.
login.signinTitle=Molimo vas da se prijavite
login.ssoSignIn=Prijavite se putem jedinstvene prijave
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

View file

@ -452,6 +452,11 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Logga in via enkel inloggning
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

View file

@ -452,6 +452,11 @@ login.locked=Hesabınız kilitlendi.
login.signinTitle=Lütfen giriş yapınız.
login.ssoSignIn=Tek Oturum Açma ile Giriş Yap
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

View file

@ -452,6 +452,11 @@ login.locked=Ваш обліковий запис заблоковано.
login.signinTitle=Будь ласка, увійдіть
login.ssoSignIn=Увійти через єдиний вхід
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

View file

@ -452,6 +452,11 @@ login.locked=您的账户已被锁定。
login.signinTitle=请登录
login.ssoSignIn=通过单点登录登录
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

View file

@ -452,6 +452,11 @@ login.locked=您的帳戶已被鎖定。
login.signinTitle=請登入
login.ssoSignIn=透過織網單一簽入
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

View file

@ -19,6 +19,23 @@ security:
# 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
# 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:
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)

View file

@ -13,8 +13,6 @@
document.addEventListener('modeChanged', function(e) {
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
switch (mode) {
@ -117,7 +115,7 @@
<h1 class="h1 mb-3 fw-normal" th:text="${@appName}">Stirling-PDF</h1>
<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>
<div th:if="${erroroauth}" class="alert alert-danger text-center">
@ -126,7 +124,7 @@
<hr />
</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>
<div class="text-danger text-center">
@ -170,5 +168,27 @@
</main>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</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>
</html>