add multi OAuth2 Provider
This commit is contained in:
parent
7b49d85804
commit
c2179ccd63
44 changed files with 1553 additions and 716 deletions
|
@ -8,11 +8,7 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContextInitializer;
|
import org.springframework.context.ApplicationContextInitializer;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
@ -62,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("#")) {
|
||||||
|
@ -70,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);
|
||||||
}
|
}
|
||||||
|
@ -78,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
|
||||||
|
@ -96,14 +93,18 @@ public class ConfigInitializer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO check parent value instead of just indent lines for duplicate keys (like enabled etc)
|
||||||
|
private static void addLine(
|
||||||
//TODO check parent value instead of just indent lines for duplicate keys (like enabled etc)
|
List<String> resultLines,
|
||||||
private static void addLine(List<String> resultLines, List<String> userLines, String templateLine, String key) {
|
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) {
|
||||||
|
@ -112,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 ->
|
||||||
|
@ -183,7 +193,8 @@ public class SecurityConfiguration {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientRegistration oidcClientRegistration() {
|
return new InMemoryClientRegistrationRepository(registrations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ClientRegistration> googleClientRegistration() {
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
return ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
|
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")
|
.registrationId("oidc")
|
||||||
.clientId(oauth.getClientId())
|
.clientId(oauth.getClientId())
|
||||||
.clientSecret(oauth.getClientSecret())
|
.clientSecret(oauth.getClientSecret())
|
||||||
.scope(oauth.getScopes())
|
.scope(oauth.getScopes())
|
||||||
.userNameAttributeName(oauth.getUseAsUsername())
|
.userNameAttributeName(oauth.getUseAsUsername())
|
||||||
.clientName("OIDC")
|
.clientName("OIDC")
|
||||||
.build();
|
.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,33 @@ 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 provider = 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;
|
||||||
|
String registrationId = oauthToken.getAuthorizedClientRegistrationId();
|
||||||
|
|
||||||
|
provider = registrationId;
|
||||||
|
logger.info(registrationId);
|
||||||
|
Provider pro;
|
||||||
|
try {
|
||||||
|
pro = oauth.getClient().get(registrationId);
|
||||||
|
issuer = pro.getIssuer();
|
||||||
|
clientId = pro.getClientId();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
provider = 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 +74,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 (provider) {
|
||||||
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();
|
||||||
|
@ -188,7 +191,6 @@ public class ApplicationProperties {
|
||||||
}
|
}
|
||||||
|
|
||||||
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,20 +263,17 @@ 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) {
|
||||||
this.useAsUsername = useAsUsername;
|
this.useAsUsername = 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
|
@ -452,6 +452,12 @@ 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=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
|
#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,12 @@ 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.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
|
#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
|
||||||
|
|
|
@ -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=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=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
|
||||||
|
|
|
@ -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=您的帳戶已被鎖定。
|
||||||
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)
|
||||||
|
|
|
@ -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>
|
Loading…
Reference in a new issue