mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-01-28 00:22:33 +01:00
wip - refactoring & cleanup of redirects and OAuth2 Providers
This commit is contained in:
parent
c298bb4928
commit
4fd2574e6a
@ -1,32 +1,27 @@
|
|||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
|
import com.coveo.saml.SamlClient;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
|
|
||||||
import com.coveo.saml.SamlClient;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import stirling.software.SPDF.SPDFApplication;
|
import stirling.software.SPDF.SPDFApplication;
|
||||||
import stirling.software.SPDF.config.security.saml2.CertificateUtils;
|
import stirling.software.SPDF.config.security.saml2.CertificateUtils;
|
||||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
||||||
import stirling.software.SPDF.model.exception.UnsupportedProviderException;
|
|
||||||
import stirling.software.SPDF.model.provider.Provider;
|
|
||||||
import stirling.software.SPDF.utils.UrlUtils;
|
import stirling.software.SPDF.utils.UrlUtils;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -36,23 +31,20 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
|||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLogoutSuccess(
|
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
if (!response.isCommitted()) {
|
if (!response.isCommitted()) {
|
||||||
// Handle user logout due to disabled account
|
// Handle user logout due to disabled account
|
||||||
if (request.getParameter("userIsDisabled") != null) {
|
if (request.getParameter("userIsDisabled") != null) {
|
||||||
response.sendRedirect(
|
response.sendRedirect(request.getContextPath() + "/login?erroroauth=userIsDisabled");
|
||||||
request.getContextPath() + "/login?erroroauth=userIsDisabled");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle OAuth2 authentication error
|
// Handle OAuth2 authentication error
|
||||||
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
|
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
|
||||||
response.sendRedirect(
|
response.sendRedirect(
|
||||||
request.getContextPath() + "/login?erroroauth=userAlreadyExistsWeb");
|
request.getContextPath() + "/login?erroroauth=userAlreadyExistsWeb");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
// Handle SAML2 logout redirection
|
// Handle SAML2 logout redirection
|
||||||
if (authentication instanceof Saml2Authentication) {
|
if (authentication instanceof Saml2Authentication) {
|
||||||
@ -66,10 +58,11 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
|||||||
else if (authentication instanceof UsernamePasswordAuthenticationToken) {
|
else if (authentication instanceof UsernamePasswordAuthenticationToken) {
|
||||||
getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
|
getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle unknown authentication types
|
// Handle unknown authentication types
|
||||||
else {
|
else {
|
||||||
log.error(
|
log.error(
|
||||||
"authentication class unknown: {}",
|
"Authentication class unknown: {}",
|
||||||
authentication.getClass().getSimpleName());
|
authentication.getClass().getSimpleName());
|
||||||
getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
|
getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
|
||||||
}
|
}
|
||||||
@ -145,28 +138,16 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
|||||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
String param = "logout=true";
|
String param = "logout=true";
|
||||||
String registrationId = null;
|
String registrationId;
|
||||||
String issuer = null;
|
String errorMessage;
|
||||||
String clientId = null;
|
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||||
|
|
||||||
if (authentication instanceof OAuth2AuthenticationToken oauthToken) {
|
if (authentication instanceof OAuth2AuthenticationToken oauthToken) {
|
||||||
registrationId = oauthToken.getAuthorizedClientRegistrationId();
|
registrationId = oauthToken.getAuthorizedClientRegistrationId();
|
||||||
|
|
||||||
try {
|
|
||||||
// Get OAuth2 provider details from configuration
|
|
||||||
Provider provider = oauth.getClient().get(registrationId);
|
|
||||||
} catch (UnsupportedProviderException e) {
|
|
||||||
log.error(e.getMessage());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
registrationId = oauth.getProvider() != null ? oauth.getProvider() : "";
|
registrationId = oauth.getProvider() != null ? oauth.getProvider() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
issuer = oauth.getIssuer();
|
|
||||||
clientId = oauth.getClientId();
|
|
||||||
String errorMessage = "";
|
|
||||||
|
|
||||||
// Handle different error scenarios during logout
|
// Handle different error scenarios during logout
|
||||||
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
|
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
|
||||||
param = "erroroauth=oauth2AuthenticationErrorWeb";
|
param = "erroroauth=oauth2AuthenticationErrorWeb";
|
||||||
@ -189,12 +170,11 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
|||||||
// Redirect based on OAuth2 provider
|
// Redirect based on OAuth2 provider
|
||||||
switch (registrationId.toLowerCase()) {
|
switch (registrationId.toLowerCase()) {
|
||||||
case "keycloak" -> {
|
case "keycloak" -> {
|
||||||
// Add Keycloak specific logout URL if needed
|
|
||||||
String logoutUrl =
|
String logoutUrl =
|
||||||
issuer
|
oauth.getIssuer()
|
||||||
+ "/protocol/openid-connect/logout"
|
+ "/protocol/openid-connect/logout"
|
||||||
+ "?client_id="
|
+ "?client_id="
|
||||||
+ clientId
|
+ oauth.getClientId()
|
||||||
+ "&post_logout_redirect_uri="
|
+ "&post_logout_redirect_uri="
|
||||||
+ response.encodeRedirectURL(redirectUrl);
|
+ response.encodeRedirectURL(redirectUrl);
|
||||||
log.info("Redirecting to Keycloak logout URL: {}", logoutUrl);
|
log.info("Redirecting to Keycloak logout URL: {}", logoutUrl);
|
||||||
@ -206,18 +186,13 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
|||||||
redirectUrl);
|
redirectUrl);
|
||||||
response.sendRedirect(redirectUrl);
|
response.sendRedirect(redirectUrl);
|
||||||
}
|
}
|
||||||
case "google" -> {
|
case "google" -> // String googleLogoutUrl =
|
||||||
// Add Google specific logout URL if needed
|
|
||||||
// String googleLogoutUrl =
|
|
||||||
// "https://accounts.google.com/Logout?continue=https://appengine.google.com/_ah/logout?continue="
|
// "https://accounts.google.com/Logout?continue=https://appengine.google.com/_ah/logout?continue="
|
||||||
// + response.encodeRedirectURL(redirectUrl);
|
// + response.encodeRedirectURL(redirectUrl);
|
||||||
log.info("Google does not have a specific logout URL");
|
|
||||||
// log.info("Redirecting to Google logout URL: " + googleLogoutUrl);
|
// log.info("Redirecting to Google logout URL: " + googleLogoutUrl);
|
||||||
// response.sendRedirect(googleLogoutUrl);
|
// response.sendRedirect(googleLogoutUrl);
|
||||||
}
|
log.info("Google does not have a specific logout URL");
|
||||||
default -> {
|
default -> {
|
||||||
// String defaultRedirectUrl = request.getContextPath() + "/login?" +
|
|
||||||
// param;
|
|
||||||
log.info("Redirecting to default logout URL: {}", redirectUrl);
|
log.info("Redirecting to default logout URL: {}", redirectUrl);
|
||||||
response.sendRedirect(redirectUrl);
|
response.sendRedirect(redirectUrl);
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,7 @@ public class SecurityConfiguration {
|
|||||||
.permitAll());
|
.permitAll());
|
||||||
}
|
}
|
||||||
// Handle OAUTH2 Logins
|
// Handle OAUTH2 Logins
|
||||||
if (applicationProperties.getSecurity().isOauth2Activ()) {
|
if (applicationProperties.getSecurity().isOauth2Active()) {
|
||||||
http.oauth2Login(
|
http.oauth2Login(
|
||||||
oauth2 ->
|
oauth2 ->
|
||||||
oauth2.loginPage("/oauth2")
|
oauth2.loginPage("/oauth2")
|
||||||
@ -257,7 +257,7 @@ public class SecurityConfiguration {
|
|||||||
.permitAll());
|
.permitAll());
|
||||||
}
|
}
|
||||||
// Handle SAML
|
// Handle SAML
|
||||||
if (applicationProperties.getSecurity().isSaml2Activ()) {
|
if (applicationProperties.getSecurity().isSaml2Active()) {
|
||||||
// && runningEE
|
// && runningEE
|
||||||
// Configure the authentication provider
|
// Configure the authentication provider
|
||||||
OpenSaml4AuthenticationProvider authenticationProvider =
|
OpenSaml4AuthenticationProvider authenticationProvider =
|
||||||
|
@ -52,7 +52,6 @@ public class CustomOAuth2AuthenticationFailureHandler
|
|||||||
log.error("OAuth2 Authentication error: " + errorCode);
|
log.error("OAuth2 Authentication error: " + errorCode);
|
||||||
log.error("OAuth2AuthenticationException", exception);
|
log.error("OAuth2AuthenticationException", exception);
|
||||||
getRedirectStrategy().sendRedirect(request, response, "/login?erroroauth=" + errorCode);
|
getRedirectStrategy().sendRedirect(request, response, "/login?erroroauth=" + errorCode);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
log.error("Unhandled authentication exception", exception);
|
log.error("Unhandled authentication exception", exception);
|
||||||
super.onAuthenticationFailure(request, response, exception);
|
super.onAuthenticationFailure(request, response, exception);
|
||||||
|
@ -25,13 +25,12 @@ import stirling.software.SPDF.utils.RequestUriUtils;
|
|||||||
public class CustomOAuth2AuthenticationSuccessHandler
|
public class CustomOAuth2AuthenticationSuccessHandler
|
||||||
extends SavedRequestAwareAuthenticationSuccessHandler {
|
extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||||
|
|
||||||
private LoginAttemptService loginAttemptService;
|
private final LoginAttemptService loginAttemptService;
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
private ApplicationProperties applicationProperties;
|
private final UserService userService;
|
||||||
private UserService userService;
|
|
||||||
|
|
||||||
public CustomOAuth2AuthenticationSuccessHandler(
|
public CustomOAuth2AuthenticationSuccessHandler(
|
||||||
final LoginAttemptService loginAttemptService,
|
LoginAttemptService loginAttemptService,
|
||||||
ApplicationProperties applicationProperties,
|
ApplicationProperties applicationProperties,
|
||||||
UserService userService) {
|
UserService userService) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
@ -75,23 +74,22 @@ public class CustomOAuth2AuthenticationSuccessHandler
|
|||||||
throw new LockedException(
|
throw new LockedException(
|
||||||
"Your account has been locked due to too many failed login attempts.");
|
"Your account has been locked due to too many failed login attempts.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userService.isUserDisabled(username)) {
|
if (userService.isUserDisabled(username)) {
|
||||||
getRedirectStrategy()
|
getRedirectStrategy()
|
||||||
.sendRedirect(request, response, "/logout?userIsDisabled=true");
|
.sendRedirect(request, response, "/logout?userIsDisabled=true");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (userService.usernameExistsIgnoreCase(username)
|
if (userService.usernameExistsIgnoreCase(username)
|
||||||
&& userService.hasPassword(username)
|
&& userService.hasPassword(username)
|
||||||
&& !userService.isAuthenticationTypeByUsername(username, AuthenticationType.SSO)
|
&& !userService.isAuthenticationTypeByUsername(username, AuthenticationType.SSO)
|
||||||
&& oAuth.getAutoCreateUser()) {
|
&& oAuth.getAutoCreateUser()) {
|
||||||
response.sendRedirect(contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
|
response.sendRedirect(contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (oAuth.getBlockRegistration()
|
if (oAuth.getBlockRegistration()
|
||||||
&& !userService.usernameExistsIgnoreCase(username)) {
|
&& !userService.usernameExistsIgnoreCase(username)) {
|
||||||
response.sendRedirect(contextPath + "/logout?oauth2_admin_blocked_user=true");
|
response.sendRedirect(contextPath + "/logout?oauth2_admin_blocked_user=true");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (principal instanceof OAuth2User) {
|
if (principal instanceof OAuth2User) {
|
||||||
userService.processSSOPostLogin(username, oAuth.getAutoCreateUser());
|
userService.processSSOPostLogin(username, oAuth.getAutoCreateUser());
|
||||||
|
@ -28,6 +28,9 @@ import stirling.software.SPDF.model.ApplicationProperties;
|
|||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
import stirling.software.SPDF.model.provider.GitHubProvider;
|
||||||
|
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||||
|
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
||||||
import stirling.software.SPDF.model.provider.Provider;
|
import stirling.software.SPDF.model.provider.Provider;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -59,17 +62,57 @@ public class OAuth2Configuration {
|
|||||||
log.error("At least one OAuth2 provider must be configured");
|
log.error("At least one OAuth2 provider must be configured");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new InMemoryClientRegistrationRepository(registrations);
|
return new InMemoryClientRegistrationRepository(registrations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<ClientRegistration> googleClientRegistration() {
|
private Optional<ClientRegistration> keycloakClientRegistration() {
|
||||||
if (isOauthOrClientEmpty()) {
|
OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2();
|
||||||
|
|
||||||
|
if (isOauthOrClientEmpty(oauth2)) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
Provider google = applicationProperties.getSecurity().getOauth2().getClient().getGoogle();
|
Client client = oauth2.getClient();
|
||||||
|
KeycloakProvider keycloakClient = client.getKeycloak();
|
||||||
|
Provider keycloak =
|
||||||
|
new KeycloakProvider(
|
||||||
|
keycloakClient.getIssuer(),
|
||||||
|
keycloakClient.getClientId(),
|
||||||
|
keycloakClient.getClientSecret(),
|
||||||
|
keycloakClient.getScopes(),
|
||||||
|
keycloakClient.getUseAsUsername());
|
||||||
|
|
||||||
return validateSettings(google)
|
return validateProvider(keycloak)
|
||||||
|
? Optional.of(
|
||||||
|
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
|
||||||
|
.registrationId(keycloak.getName())
|
||||||
|
.clientId(keycloak.getClientId())
|
||||||
|
.clientSecret(keycloak.getClientSecret())
|
||||||
|
.scope(keycloak.getScopes())
|
||||||
|
.userNameAttributeName(keycloak.getUseAsUsername())
|
||||||
|
.clientName(keycloak.getClientName())
|
||||||
|
.build())
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ClientRegistration> googleClientRegistration() {
|
||||||
|
OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2();
|
||||||
|
|
||||||
|
if (isOauthOrClientEmpty(oauth2)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Client client = oauth2.getClient();
|
||||||
|
GoogleProvider googleClient = client.getGoogle();
|
||||||
|
Provider google =
|
||||||
|
new GoogleProvider(
|
||||||
|
googleClient.getClientId(),
|
||||||
|
googleClient.getClientSecret(),
|
||||||
|
googleClient.getScopes(),
|
||||||
|
googleClient.getUseAsUsername());
|
||||||
|
|
||||||
|
return validateProvider(google)
|
||||||
? Optional.of(
|
? Optional.of(
|
||||||
ClientRegistration.withRegistrationId(google.getName())
|
ClientRegistration.withRegistrationId(google.getName())
|
||||||
.clientId(google.getClientId())
|
.clientId(google.getClientId())
|
||||||
@ -86,35 +129,23 @@ public class OAuth2Configuration {
|
|||||||
: Optional.empty();
|
: Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<ClientRegistration> keycloakClientRegistration() {
|
|
||||||
if (isOauthOrClientEmpty()) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
Provider keycloak =
|
|
||||||
applicationProperties.getSecurity().getOauth2().getClient().getKeycloak();
|
|
||||||
|
|
||||||
return validateSettings(keycloak)
|
|
||||||
? Optional.of(
|
|
||||||
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
|
|
||||||
.registrationId(keycloak.getName())
|
|
||||||
.clientId(keycloak.getClientId())
|
|
||||||
.clientSecret(keycloak.getClientSecret())
|
|
||||||
.scope(keycloak.getScopes())
|
|
||||||
.userNameAttributeName(keycloak.getUseAsUsername())
|
|
||||||
.clientName(keycloak.getClientName())
|
|
||||||
.build())
|
|
||||||
: Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<ClientRegistration> githubClientRegistration() {
|
private Optional<ClientRegistration> githubClientRegistration() {
|
||||||
if (isOauthOrClientEmpty()) {
|
OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2();
|
||||||
|
|
||||||
|
if (isOauthOrClientEmpty(oauth2)) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
Provider github = applicationProperties.getSecurity().getOauth2().getClient().getGithub();
|
Client client = oauth2.getClient();
|
||||||
|
GitHubProvider githubClient = client.getGithub();
|
||||||
|
Provider github =
|
||||||
|
new GitHubProvider(
|
||||||
|
githubClient.getClientId(),
|
||||||
|
githubClient.getClientSecret(),
|
||||||
|
githubClient.getScopes(),
|
||||||
|
githubClient.getUseAsUsername());
|
||||||
|
|
||||||
return validateSettings(github)
|
return validateProvider(github)
|
||||||
? Optional.of(
|
? Optional.of(
|
||||||
ClientRegistration.withRegistrationId(github.getName())
|
ClientRegistration.withRegistrationId(github.getName())
|
||||||
.clientId(github.getClientId())
|
.clientId(github.getClientId())
|
||||||
@ -134,42 +165,41 @@ public class OAuth2Configuration {
|
|||||||
private Optional<ClientRegistration> oidcClientRegistration() {
|
private Optional<ClientRegistration> oidcClientRegistration() {
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||||
|
|
||||||
if (oauth == null
|
if (isOauthOrClientEmpty(oauth)) {
|
||||||
|| 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.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isStringEmpty(oauth.getIssuer())
|
||||||
|
|| isStringEmpty(oauth.getClientId())
|
||||||
|
|| isStringEmpty(oauth.getClientSecret())
|
||||||
|
|| isCollectionEmpty(oauth.getScopes())
|
||||||
|
|| isStringEmpty(oauth.getUseAsUsername())) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = oauth.getProvider();
|
||||||
|
String firstChar = String.valueOf(name.charAt(0));
|
||||||
|
String clientName = name.replaceFirst(firstChar, firstChar.toUpperCase());
|
||||||
|
|
||||||
return Optional.of(
|
return Optional.of(
|
||||||
ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
|
ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
|
||||||
.registrationId("oidc")
|
.registrationId(name)
|
||||||
.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(clientName)
|
||||||
.redirectUri(REDIRECT_URI_PATH + "oidc")
|
.redirectUri(REDIRECT_URI_PATH + name)
|
||||||
.authorizationGrantType(AUTHORIZATION_CODE)
|
.authorizationGrantType(AUTHORIZATION_CODE)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isOauthOrClientEmpty() {
|
private boolean isOauthOrClientEmpty(OAUTH2 oauth) {
|
||||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
|
||||||
|
|
||||||
if (oauth == null || !oauth.getEnabled()) {
|
if (oauth == null || !oauth.getEnabled()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Client client = oauth.getClient();
|
Client client = oauth.getClient();
|
||||||
|
|
||||||
return client == null;
|
return client == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,11 +232,9 @@ public class OAuth2Configuration {
|
|||||||
(String) oauth2Auth.getAttributes().get(useAsUsername));
|
(String) oauth2Auth.getAttributes().get(useAsUsername));
|
||||||
if (userOpt.isPresent()) {
|
if (userOpt.isPresent()) {
|
||||||
User user = userOpt.get();
|
User user = userOpt.get();
|
||||||
if (user != null) {
|
mappedAuthorities.add(
|
||||||
mappedAuthorities.add(
|
new SimpleGrantedAuthority(
|
||||||
new SimpleGrantedAuthority(
|
userService.findRole(user).getAuthority()));
|
||||||
userService.findRole(user).getAuthority()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
import static stirling.software.SPDF.utils.validation.Validator.validateSettings;
|
import static stirling.software.SPDF.utils.validation.Validator.validateProvider;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
@ -40,12 +40,11 @@ import stirling.software.SPDF.repository.UserRepository;
|
|||||||
public class AccountWebController {
|
public class AccountWebController {
|
||||||
|
|
||||||
public static final String OAUTH_2_AUTHORIZATION = "/oauth2/authorization/";
|
public static final String OAUTH_2_AUTHORIZATION = "/oauth2/authorization/";
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
private final SessionPersistentRegistry sessionPersistentRegistry;
|
private final SessionPersistentRegistry sessionPersistentRegistry;
|
||||||
|
// Assuming you have a repository for user operations
|
||||||
private final UserRepository // Assuming you have a repository for user operations
|
private final UserRepository userRepository;
|
||||||
userRepository;
|
|
||||||
|
|
||||||
public AccountWebController(
|
public AccountWebController(
|
||||||
ApplicationProperties applicationProperties,
|
ApplicationProperties applicationProperties,
|
||||||
@ -70,7 +69,10 @@ public class AccountWebController {
|
|||||||
if (oauth != null) {
|
if (oauth != null) {
|
||||||
if (oauth.getEnabled()) {
|
if (oauth.getEnabled()) {
|
||||||
if (oauth.isSettingsValid()) {
|
if (oauth.isSettingsValid()) {
|
||||||
providerList.put(OAUTH_2_AUTHORIZATION + "oidc", oauth.getProvider());
|
String firstChar = String.valueOf(oauth.getProvider().charAt(0));
|
||||||
|
String clientName =
|
||||||
|
oauth.getProvider().replaceFirst(firstChar, firstChar.toUpperCase());
|
||||||
|
providerList.put(OAUTH_2_AUTHORIZATION + "oidc", clientName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Client client = oauth.getClient();
|
Client client = oauth.getClient();
|
||||||
@ -78,21 +80,21 @@ public class AccountWebController {
|
|||||||
if (client != null) {
|
if (client != null) {
|
||||||
GoogleProvider google = client.getGoogle();
|
GoogleProvider google = client.getGoogle();
|
||||||
|
|
||||||
if (validateSettings(google)) {
|
if (validateProvider(google)) {
|
||||||
providerList.put(
|
providerList.put(
|
||||||
OAUTH_2_AUTHORIZATION + google.getName(), google.getClientName());
|
OAUTH_2_AUTHORIZATION + google.getName(), google.getClientName());
|
||||||
}
|
}
|
||||||
|
|
||||||
GitHubProvider github = client.getGithub();
|
GitHubProvider github = client.getGithub();
|
||||||
|
|
||||||
if (validateSettings(github)) {
|
if (validateProvider(github)) {
|
||||||
providerList.put(
|
providerList.put(
|
||||||
OAUTH_2_AUTHORIZATION + github.getName(), github.getClientName());
|
OAUTH_2_AUTHORIZATION + github.getName(), github.getClientName());
|
||||||
}
|
}
|
||||||
|
|
||||||
KeycloakProvider keycloak = client.getKeycloak();
|
KeycloakProvider keycloak = client.getKeycloak();
|
||||||
|
|
||||||
if (validateSettings(keycloak)) {
|
if (validateProvider(keycloak)) {
|
||||||
providerList.put(
|
providerList.put(
|
||||||
OAUTH_2_AUTHORIZATION + keycloak.getName(),
|
OAUTH_2_AUTHORIZATION + keycloak.getName(),
|
||||||
keycloak.getClientName());
|
keycloak.getClientName());
|
||||||
@ -103,7 +105,7 @@ public class AccountWebController {
|
|||||||
|
|
||||||
SAML2 saml2 = securityProps.getSaml2();
|
SAML2 saml2 = securityProps.getSaml2();
|
||||||
|
|
||||||
if (securityProps.isSaml2Activ()
|
if (securityProps.isSaml2Active()
|
||||||
&& applicationProperties.getSystem().getEnableAlphaFunctionality()) {
|
&& applicationProperties.getSystem().getEnableAlphaFunctionality()) {
|
||||||
providerList.put("/saml2/authenticate/" + saml2.getRegistrationId(), "SAML 2");
|
providerList.put("/saml2/authenticate/" + saml2.getRegistrationId(), "SAML 2");
|
||||||
}
|
}
|
||||||
@ -321,40 +323,28 @@ public class AccountWebController {
|
|||||||
if (authentication != null && authentication.isAuthenticated()) {
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
Object principal = authentication.getPrincipal();
|
Object principal = authentication.getPrincipal();
|
||||||
String username = null;
|
String username = null;
|
||||||
if (principal instanceof UserDetails) {
|
if (principal instanceof UserDetails userDetails) {
|
||||||
// Cast the principal object to UserDetails
|
|
||||||
UserDetails userDetails = (UserDetails) principal;
|
|
||||||
// Retrieve username and other attributes
|
// Retrieve username and other attributes
|
||||||
username = userDetails.getUsername();
|
username = userDetails.getUsername();
|
||||||
// Add oAuth2 Login attributes to the model
|
// Add oAuth2 Login attributes to the model
|
||||||
model.addAttribute("oAuth2Login", false);
|
model.addAttribute("oAuth2Login", false);
|
||||||
}
|
}
|
||||||
if (principal instanceof OAuth2User) {
|
if (principal instanceof OAuth2User userDetails) {
|
||||||
// Cast the principal object to OAuth2User
|
|
||||||
OAuth2User userDetails = (OAuth2User) principal;
|
|
||||||
// Retrieve username and other attributes
|
// Retrieve username and other attributes
|
||||||
username =
|
username = userDetails.getName();
|
||||||
userDetails.getAttribute(
|
|
||||||
applicationProperties.getSecurity().getOauth2().getUseAsUsername());
|
|
||||||
// Add oAuth2 Login attributes to the model
|
// Add oAuth2 Login attributes to the model
|
||||||
model.addAttribute("oAuth2Login", true);
|
model.addAttribute("oAuth2Login", true);
|
||||||
}
|
}
|
||||||
if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
|
if (principal instanceof CustomSaml2AuthenticatedPrincipal userDetails) {
|
||||||
// Cast the principal object to OAuth2User
|
|
||||||
CustomSaml2AuthenticatedPrincipal userDetails =
|
|
||||||
(CustomSaml2AuthenticatedPrincipal) principal;
|
|
||||||
// Retrieve username and other attributes
|
// Retrieve username and other attributes
|
||||||
username = userDetails.getName();
|
username = userDetails.getName();
|
||||||
// Add oAuth2 Login attributes to the model
|
// Add oAuth2 Login attributes to the model
|
||||||
model.addAttribute("oAuth2Login", true);
|
model.addAttribute("oAuth2Login", true);
|
||||||
}
|
}
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
// Fetch user details from the database
|
// Fetch user details from the database, assuming findByUsername method exists
|
||||||
Optional<User> user =
|
Optional<User> user = userRepository.findByUsernameIgnoreCaseWithSettings(username);
|
||||||
userRepository
|
|
||||||
.findByUsernameIgnoreCaseWithSettings( // Assuming findByUsername
|
|
||||||
// method exists
|
|
||||||
username);
|
|
||||||
if (!user.isPresent()) {
|
if (!user.isPresent()) {
|
||||||
return "redirect:/error";
|
return "redirect:/error";
|
||||||
}
|
}
|
||||||
@ -368,31 +358,20 @@ public class AccountWebController {
|
|||||||
log.error("exception", e);
|
log.error("exception", e);
|
||||||
return "redirect:/error";
|
return "redirect:/error";
|
||||||
}
|
}
|
||||||
|
|
||||||
String messageType = request.getParameter("messageType");
|
String messageType = request.getParameter("messageType");
|
||||||
if (messageType != null) {
|
if (messageType != null) {
|
||||||
switch (messageType) {
|
switch (messageType) {
|
||||||
case "notAuthenticated":
|
case "notAuthenticated" -> messageType = "notAuthenticatedMessage";
|
||||||
messageType = "notAuthenticatedMessage";
|
case "userNotFound" -> messageType = "userNotFoundMessage";
|
||||||
break;
|
case "incorrectPassword" -> messageType = "incorrectPasswordMessage";
|
||||||
case "userNotFound":
|
case "usernameExists" -> messageType = "usernameExistsMessage";
|
||||||
messageType = "userNotFoundMessage";
|
case "invalidUsername" -> messageType = "invalidUsernameMessage";
|
||||||
break;
|
|
||||||
case "incorrectPassword":
|
|
||||||
messageType = "incorrectPasswordMessage";
|
|
||||||
break;
|
|
||||||
case "usernameExists":
|
|
||||||
messageType = "usernameExistsMessage";
|
|
||||||
break;
|
|
||||||
case "invalidUsername":
|
|
||||||
messageType = "invalidUsernameMessage";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
model.addAttribute("messageType", messageType);
|
|
||||||
}
|
}
|
||||||
// Add attributes to the model
|
// Add attributes to the model
|
||||||
model.addAttribute("username", username);
|
model.addAttribute("username", username);
|
||||||
|
model.addAttribute("messageType", messageType);
|
||||||
model.addAttribute("role", user.get().getRolesAsString());
|
model.addAttribute("role", user.get().getRolesAsString());
|
||||||
model.addAttribute("settings", settingsJson);
|
model.addAttribute("settings", settingsJson);
|
||||||
model.addAttribute("changeCredsFlag", user.get().isFirstLogin());
|
model.addAttribute("changeCredsFlag", user.get().isFirstLogin());
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package stirling.software.SPDF.model;
|
package stirling.software.SPDF.model;
|
||||||
|
|
||||||
|
import static stirling.software.SPDF.utils.validation.Validator.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -49,11 +51,11 @@ public class ApplicationProperties {
|
|||||||
public PropertySource<?> dynamicYamlPropertySource(ConfigurableEnvironment environment)
|
public PropertySource<?> dynamicYamlPropertySource(ConfigurableEnvironment environment)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
String configPath = InstallationPathConfig.getSettingsPath();
|
String configPath = InstallationPathConfig.getSettingsPath();
|
||||||
log.debug("Attempting to load settings from: " + configPath);
|
log.debug("Attempting to load settings from: {}", configPath);
|
||||||
|
|
||||||
File file = new File(configPath);
|
File file = new File(configPath);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
log.error("Warning: Settings file does not exist at: " + configPath);
|
log.error("Warning: Settings file does not exist at: {}", configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource resource = new FileSystemResource(configPath);
|
Resource resource = new FileSystemResource(configPath);
|
||||||
@ -66,7 +68,7 @@ public class ApplicationProperties {
|
|||||||
new YamlPropertySourceFactory().createPropertySource(null, encodedResource);
|
new YamlPropertySourceFactory().createPropertySource(null, encodedResource);
|
||||||
environment.getPropertySources().addFirst(propertySource);
|
environment.getPropertySources().addFirst(propertySource);
|
||||||
|
|
||||||
log.debug("Loaded properties: " + propertySource.getSource());
|
log.debug("Loaded properties: {}", propertySource.getSource());
|
||||||
|
|
||||||
return propertySource;
|
return propertySource;
|
||||||
}
|
}
|
||||||
@ -135,13 +137,13 @@ public class ApplicationProperties {
|
|||||||
|| loginMethod.equalsIgnoreCase(LoginMethods.ALL.toString()));
|
|| loginMethod.equalsIgnoreCase(LoginMethods.ALL.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOauth2Activ() {
|
public boolean isOauth2Active() {
|
||||||
return (oauth2 != null
|
return (oauth2 != null
|
||||||
&& oauth2.getEnabled()
|
&& oauth2.getEnabled()
|
||||||
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
|
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSaml2Activ() {
|
public boolean isSaml2Active() {
|
||||||
return (saml2 != null
|
return (saml2 != null
|
||||||
&& saml2.getEnabled()
|
&& saml2.getEnabled()
|
||||||
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
|
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
|
||||||
@ -240,11 +242,11 @@ public class ApplicationProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSettingsValid() {
|
public boolean isSettingsValid() {
|
||||||
return isValid(this.getIssuer(), "issuer")
|
return isStringEmpty(this.getIssuer())
|
||||||
&& isValid(this.getClientId(), "clientId")
|
&& isStringEmpty(this.getClientId())
|
||||||
&& isValid(this.getClientSecret(), "clientSecret")
|
&& isStringEmpty(this.getClientSecret())
|
||||||
&& isValid(this.getScopes(), "scopes")
|
&& isCollectionEmpty(this.getScopes())
|
||||||
&& isValid(this.getUseAsUsername(), "useAsUsername");
|
&& isStringEmpty(this.getUseAsUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@ -262,7 +264,8 @@ public class ApplicationProperties {
|
|||||||
throw new UnsupportedProviderException(
|
throw new UnsupportedProviderException(
|
||||||
"Logout from the provider "
|
"Logout from the provider "
|
||||||
+ registrationId
|
+ registrationId
|
||||||
+ " is not supported. Report it at https://github.com/Stirling-Tools/Stirling-PDF/issues");
|
+ " is not supported. "
|
||||||
|
+ "Report it at https://github.com/Stirling-Tools/Stirling-PDF/issues");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,15 @@ public class GitHubProvider extends Provider {
|
|||||||
private static final String TOKEN_URI = "https://github.com/login/oauth/access_token";
|
private static final String TOKEN_URI = "https://github.com/login/oauth/access_token";
|
||||||
private static final String USER_INFO_URI = "https://api.github.com/user";
|
private static final String USER_INFO_URI = "https://api.github.com/user";
|
||||||
|
|
||||||
public GitHubProvider(String clientId, String clientSecret, String useAsUsername) {
|
public GitHubProvider(
|
||||||
|
String clientId, String clientSecret, Collection<String> scopes, String useAsUsername) {
|
||||||
super(
|
super(
|
||||||
null,
|
null,
|
||||||
NAME,
|
NAME,
|
||||||
CLIENT_NAME,
|
CLIENT_NAME,
|
||||||
clientId,
|
clientId,
|
||||||
clientSecret,
|
clientSecret,
|
||||||
new ArrayList<>(),
|
scopes,
|
||||||
useAsUsername != null ? useAsUsername : "login",
|
useAsUsername != null ? useAsUsername : "login",
|
||||||
AUTHORIZATION_URI,
|
AUTHORIZATION_URI,
|
||||||
TOKEN_URI,
|
TOKEN_URI,
|
||||||
@ -70,7 +71,7 @@ public class GitHubProvider extends Provider {
|
|||||||
return "GitHub [clientId="
|
return "GitHub [clientId="
|
||||||
+ getClientId()
|
+ getClientId()
|
||||||
+ ", clientSecret="
|
+ ", clientSecret="
|
||||||
+ (getClientSecret() != null && !getClientSecret().isEmpty() ? "MASKED" : "NULL")
|
+ (getClientSecret() != null && !getClientSecret().isEmpty() ? "*****" : "NULL")
|
||||||
+ ", scopes="
|
+ ", scopes="
|
||||||
+ getScopes()
|
+ getScopes()
|
||||||
+ ", useAsUsername="
|
+ ", useAsUsername="
|
||||||
|
@ -15,14 +15,15 @@ public class GoogleProvider extends Provider {
|
|||||||
private static final String USER_INFO_URI =
|
private static final String USER_INFO_URI =
|
||||||
"https://www.googleapis.com/oauth2/v3/userinfo?alt=json";
|
"https://www.googleapis.com/oauth2/v3/userinfo?alt=json";
|
||||||
|
|
||||||
public GoogleProvider(String clientId, String clientSecret, String useAsUsername) {
|
public GoogleProvider(
|
||||||
|
String clientId, String clientSecret, Collection<String> scopes, String useAsUsername) {
|
||||||
super(
|
super(
|
||||||
null,
|
null,
|
||||||
NAME,
|
NAME,
|
||||||
CLIENT_NAME,
|
CLIENT_NAME,
|
||||||
clientId,
|
clientId,
|
||||||
clientSecret,
|
clientSecret,
|
||||||
new ArrayList<>(),
|
scopes,
|
||||||
useAsUsername,
|
useAsUsername,
|
||||||
AUTHORIZATION_URI,
|
AUTHORIZATION_URI,
|
||||||
TOKEN_URI,
|
TOKEN_URI,
|
||||||
@ -69,7 +70,7 @@ public class GoogleProvider extends Provider {
|
|||||||
return "Google [clientId="
|
return "Google [clientId="
|
||||||
+ getClientId()
|
+ getClientId()
|
||||||
+ ", clientSecret="
|
+ ", clientSecret="
|
||||||
+ (getClientSecret() != null && !getClientSecret().isEmpty() ? "MASKED" : "NULL")
|
+ (getClientSecret() != null && !getClientSecret().isEmpty() ? "*****" : "NULL")
|
||||||
+ ", scopes="
|
+ ", scopes="
|
||||||
+ getScopes()
|
+ getScopes()
|
||||||
+ ", useAsUsername="
|
+ ", useAsUsername="
|
||||||
|
@ -12,14 +12,18 @@ public class KeycloakProvider extends Provider {
|
|||||||
private static final String CLIENT_NAME = "Keycloak";
|
private static final String CLIENT_NAME = "Keycloak";
|
||||||
|
|
||||||
public KeycloakProvider(
|
public KeycloakProvider(
|
||||||
String issuer, String clientId, String clientSecret, String useAsUsername) {
|
String issuer,
|
||||||
|
String clientId,
|
||||||
|
String clientSecret,
|
||||||
|
Collection<String> scopes,
|
||||||
|
String useAsUsername) {
|
||||||
super(
|
super(
|
||||||
issuer,
|
issuer,
|
||||||
NAME,
|
NAME,
|
||||||
CLIENT_NAME,
|
CLIENT_NAME,
|
||||||
clientId,
|
clientId,
|
||||||
clientSecret,
|
clientSecret,
|
||||||
new ArrayList<>(),
|
scopes,
|
||||||
useAsUsername,
|
useAsUsername,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@ -38,7 +42,7 @@ public class KeycloakProvider extends Provider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> getScopes() {
|
public Collection<String> getScopes() {
|
||||||
var scopes = super.getScopes();
|
Collection<String> scopes = super.getScopes();
|
||||||
|
|
||||||
if (scopes == null || scopes.isEmpty()) {
|
if (scopes == null || scopes.isEmpty()) {
|
||||||
scopes = new ArrayList<>();
|
scopes = new ArrayList<>();
|
||||||
@ -56,7 +60,7 @@ public class KeycloakProvider extends Provider {
|
|||||||
+ ", clientId="
|
+ ", clientId="
|
||||||
+ getClientId()
|
+ getClientId()
|
||||||
+ ", clientSecret="
|
+ ", clientSecret="
|
||||||
+ (getClientSecret() != null && !getClientSecret().isBlank() ? "MASKED" : "NULL")
|
+ (getClientSecret() != null && !getClientSecret().isBlank() ? "*****" : "NULL")
|
||||||
+ ", scopes="
|
+ ", scopes="
|
||||||
+ getScopes()
|
+ getScopes()
|
||||||
+ ", useAsUsername="
|
+ ", useAsUsername="
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package stirling.software.SPDF.model.provider;
|
package stirling.software.SPDF.model.provider;
|
||||||
|
|
||||||
|
import static stirling.software.SPDF.utils.validation.Validator.isStringEmpty;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -9,7 +12,7 @@ import lombok.NoArgsConstructor;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public abstract class Provider {
|
public class Provider {
|
||||||
|
|
||||||
private String issuer;
|
private String issuer;
|
||||||
private String name;
|
private String name;
|
||||||
@ -38,8 +41,8 @@ public abstract class Provider {
|
|||||||
this.clientName = clientName;
|
this.clientName = clientName;
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
this.clientSecret = clientSecret;
|
this.clientSecret = clientSecret;
|
||||||
this.scopes = scopes;
|
this.scopes = scopes == null ? new ArrayList<>() : scopes;
|
||||||
this.useAsUsername = !useAsUsername.isBlank() ? useAsUsername : "email";
|
this.useAsUsername = isStringEmpty(useAsUsername) ? "email" : useAsUsername;
|
||||||
this.authorizationUri = authorizationUri;
|
this.authorizationUri = authorizationUri;
|
||||||
this.tokenUri = tokenUri;
|
this.tokenUri = tokenUri;
|
||||||
this.userInfoUri = userInfoUri;
|
this.userInfoUri = userInfoUri;
|
||||||
@ -51,4 +54,23 @@ public abstract class Provider {
|
|||||||
Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList());
|
Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Provider [issuer="
|
||||||
|
+ getIssuer()
|
||||||
|
+ ", name="
|
||||||
|
+ getName()
|
||||||
|
+ ", clientName="
|
||||||
|
+ getClientName()
|
||||||
|
+ ", clientId="
|
||||||
|
+ getClientId()
|
||||||
|
+ ", clientSecret="
|
||||||
|
+ (getClientSecret() != null && !getClientSecret().isEmpty() ? "*****" : "NULL")
|
||||||
|
+ ", scopes="
|
||||||
|
+ getScopes()
|
||||||
|
+ ", useAsUsername="
|
||||||
|
+ getUseAsUsername()
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import stirling.software.SPDF.model.provider.Provider;
|
|||||||
|
|
||||||
public class Validator {
|
public class Validator {
|
||||||
|
|
||||||
public static boolean validateSettings(Provider provider) {
|
public static boolean validateProvider(Provider provider) {
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -23,14 +23,18 @@ public class Validator {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isStringEmpty(provider.getUseAsUsername());
|
if (isStringEmpty(provider.getUseAsUsername())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isStringEmpty(String input) {
|
public static boolean isStringEmpty(String input) {
|
||||||
return input == null || input.isBlank();
|
return input == null || input.isBlank();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isCollectionEmpty(Collection<String> input) {
|
public static boolean isCollectionEmpty(Collection<String> input) {
|
||||||
return input == null || input.isEmpty();
|
return input == null || input.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,20 +30,22 @@ class ValidatorTest {
|
|||||||
when(provider.getScopes()).thenReturn(List.of("read:user"));
|
when(provider.getScopes()).thenReturn(List.of("read:user"));
|
||||||
when(provider.getUseAsUsername()).thenReturn("email");
|
when(provider.getUseAsUsername()).thenReturn("email");
|
||||||
|
|
||||||
assertTrue(Validator.validateSettings(provider));
|
assertTrue(Validator.validateProvider(provider));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("providerParams")
|
@MethodSource("providerParams")
|
||||||
void testUnsuccessfulValidation(Provider provider) {
|
void testUnsuccessfulValidation(Provider provider) {
|
||||||
assertFalse(Validator.validateSettings(provider));
|
assertFalse(Validator.validateProvider(provider));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream<Arguments> providerParams() {
|
public static Stream<Arguments> providerParams() {
|
||||||
var generic = new GitHubProvider(null, "clientSecret", " ");
|
Provider generic = null;
|
||||||
var google = new GoogleProvider(null, "clientSecret", "email");
|
var google = new GoogleProvider(null, "clientSecret", List.of("scope"), "email");
|
||||||
var github = new GitHubProvider("clientId", "", "email");
|
var github = new GitHubProvider("clientId", "", List.of("scope"), "login");
|
||||||
var keycloak = new KeycloakProvider("issuer", "clientId", "clientSecret", " ");
|
var keycloak = new KeycloakProvider("issuer", "clientId", "clientSecret", List.of("scope"), "email");
|
||||||
|
|
||||||
|
keycloak.setUseAsUsername(null);
|
||||||
|
|
||||||
return Stream.of(
|
return Stream.of(
|
||||||
Arguments.of(generic),
|
Arguments.of(generic),
|
||||||
|
Loading…
Reference in New Issue
Block a user