diff --git a/src/main/java/stirling/software/SPDF/config/interfaces/DatabaseInterface.java b/src/main/java/stirling/software/SPDF/config/interfaces/DatabaseInterface.java index 18fbf7883..34b457e89 100644 --- a/src/main/java/stirling/software/SPDF/config/interfaces/DatabaseInterface.java +++ b/src/main/java/stirling/software/SPDF/config/interfaces/DatabaseInterface.java @@ -3,7 +3,7 @@ package stirling.software.SPDF.config.interfaces; import java.sql.SQLException; import java.util.List; -import stirling.software.SPDF.model.provider.UnsupportedProviderException; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; import stirling.software.SPDF.utils.FileInfo; public interface DatabaseInterface { diff --git a/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java index 067c8bde0..18a91a79c 100644 --- a/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java +++ b/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java @@ -15,7 +15,6 @@ import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuc import com.coveo.saml.SamlClient; -import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -28,8 +27,8 @@ import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrin import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2; import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2; -import stirling.software.SPDF.model.Provider; -import stirling.software.SPDF.model.provider.UnsupportedProviderException; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; +import stirling.software.SPDF.model.provider.Provider; import stirling.software.SPDF.utils.UrlUtils; @Slf4j @@ -41,7 +40,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { @Override public void onLogoutSuccess( HttpServletRequest request, HttpServletResponse response, Authentication authentication) - throws IOException, ServletException { + throws IOException { if (!response.isCommitted()) { // Handle user logout due to disabled account @@ -60,30 +59,25 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { // Handle SAML2 logout redirection if (authentication instanceof Saml2Authentication) { getRedirect_saml2(request, response, authentication); - return; } // Handle OAuth2 logout redirection else if (authentication instanceof OAuth2AuthenticationToken) { getRedirect_oauth2(request, response, authentication); - return; } // Handle Username/Password logout else if (authentication instanceof UsernamePasswordAuthenticationToken) { getRedirectStrategy().sendRedirect(request, response, "/login?logout=true"); - return; } // Handle unknown authentication types else { log.error( - "authentication class unknown: " - + authentication.getClass().getSimpleName()); + "authentication class unknown: {}", + authentication.getClass().getSimpleName()); getRedirectStrategy().sendRedirect(request, response, "/login?logout=true"); - return; } } else { // Redirect to login page after logout getRedirectStrategy().sendRedirect(request, response, "/login?logout=true"); - return; } } } @@ -164,17 +158,17 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { try { // Get OAuth2 provider details from configuration Provider provider = oauth.getClient().get(registrationId); - issuer = provider.getIssuer(); - clientId = provider.getClientId(); } catch (UnsupportedProviderException e) { log.error(e.getMessage()); } } else { registrationId = oauth.getProvider() != null ? oauth.getProvider() : ""; - issuer = oauth.getIssuer(); - clientId = oauth.getClientId(); } + + issuer = oauth.getIssuer(); + clientId = oauth.getClientId(); String errorMessage = ""; + // Handle different error scenarios during logout if (request.getParameter("oauth2AuthenticationErrorWeb") != null) { param = "erroroauth=oauth2AuthenticationErrorWeb"; @@ -196,7 +190,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { // Redirect based on OAuth2 provider switch (registrationId.toLowerCase()) { - case "keycloak": + case "keycloak" -> { // Add Keycloak specific logout URL if needed String logoutUrl = issuer @@ -207,27 +201,28 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { + response.encodeRedirectURL(redirect_url); log.info("Redirecting to Keycloak logout URL: " + logoutUrl); response.sendRedirect(logoutUrl); - break; - case "github": + } + case "github" -> { // Add GitHub specific logout URL if needed + // todo: why does the redirect go to github? shouldn't it come to Stirling PDF? String githubLogoutUrl = "https://github.com/logout"; - log.info("Redirecting to GitHub logout URL: " + githubLogoutUrl); - response.sendRedirect(githubLogoutUrl); - break; - case "google": + log.info("Redirecting to GitHub logout URL: " + redirect_url); + response.sendRedirect(redirect_url); + } + case "google" -> { // Add Google specific logout URL if needed // String googleLogoutUrl = // "https://accounts.google.com/Logout?continue=https://appengine.google.com/_ah/logout?continue=" // + response.encodeRedirectURL(redirect_url); log.info("Google does not have a specific logout URL"); - // log.info("Redirecting to Google logout URL: " + googleLogoutUrl); - // response.sendRedirect(googleLogoutUrl); - // break; - default: + // log.info("Redirecting to Google logout URL: " + googleLogoutUrl); + // response.sendRedirect(googleLogoutUrl); + } + default -> { String defaultRedirectUrl = request.getContextPath() + "/login?" + param; - log.info("Redirecting to default logout URL: " + defaultRedirectUrl); + log.info("Redirecting to default logout URL: {}", defaultRedirectUrl); response.sendRedirect(defaultRedirectUrl); - break; + } } } diff --git a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java index 432e2d2fc..261fc307e 100644 --- a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java +++ b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java @@ -12,7 +12,7 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.config.interfaces.DatabaseInterface; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.Role; -import stirling.software.SPDF.model.provider.UnsupportedProviderException; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; @Slf4j @Component diff --git a/src/main/java/stirling/software/SPDF/config/security/UserService.java b/src/main/java/stirling/software/SPDF/config/security/UserService.java index bd1962f6f..5a2730062 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserService.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserService.java @@ -27,7 +27,7 @@ import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrin import stirling.software.SPDF.config.security.session.SessionPersistentRegistry; import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface; import stirling.software.SPDF.model.*; -import stirling.software.SPDF.model.provider.UnsupportedProviderException; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; import stirling.software.SPDF.repository.AuthorityRepository; import stirling.software.SPDF.repository.UserRepository; diff --git a/src/main/java/stirling/software/SPDF/config/security/database/DatabaseConfig.java b/src/main/java/stirling/software/SPDF/config/security/database/DatabaseConfig.java index d2b301c01..704fd013d 100644 --- a/src/main/java/stirling/software/SPDF/config/security/database/DatabaseConfig.java +++ b/src/main/java/stirling/software/SPDF/config/security/database/DatabaseConfig.java @@ -14,7 +14,7 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.config.InstallationPathConfig; import stirling.software.SPDF.model.ApplicationProperties; -import stirling.software.SPDF.model.provider.UnsupportedProviderException; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; @Slf4j @Getter diff --git a/src/main/java/stirling/software/SPDF/config/security/database/ScheduledTasks.java b/src/main/java/stirling/software/SPDF/config/security/database/ScheduledTasks.java index 8597935f5..c318f1d97 100644 --- a/src/main/java/stirling/software/SPDF/config/security/database/ScheduledTasks.java +++ b/src/main/java/stirling/software/SPDF/config/security/database/ScheduledTasks.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Component; import stirling.software.SPDF.config.interfaces.DatabaseInterface; import stirling.software.SPDF.controller.api.H2SQLCondition; -import stirling.software.SPDF.model.provider.UnsupportedProviderException; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; @Component @Conditional(H2SQLCondition.class) diff --git a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java index 75b7ec006..c3b9acec7 100644 --- a/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java +++ b/src/main/java/stirling/software/SPDF/config/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java @@ -20,7 +20,7 @@ import stirling.software.SPDF.config.security.UserService; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2; import stirling.software.SPDF.model.AuthenticationType; -import stirling.software.SPDF.model.provider.UnsupportedProviderException; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; import stirling.software.SPDF.utils.RequestUriUtils; public class CustomOAuth2AuthenticationSuccessHandler diff --git a/src/main/java/stirling/software/SPDF/config/security/oauth2/OAuth2Configuration.java b/src/main/java/stirling/software/SPDF/config/security/oauth2/OAuth2Configuration.java index 77c5313ae..c252e67d7 100644 --- a/src/main/java/stirling/software/SPDF/config/security/oauth2/OAuth2Configuration.java +++ b/src/main/java/stirling/software/SPDF/config/security/oauth2/OAuth2Configuration.java @@ -1,5 +1,7 @@ package stirling.software.SPDF.config.security.oauth2; +import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -30,11 +32,13 @@ import stirling.software.SPDF.model.provider.GithubProvider; import stirling.software.SPDF.model.provider.GoogleProvider; import stirling.software.SPDF.model.provider.KeycloakProvider; -@Configuration @Slf4j +@Configuration @ConditionalOnProperty(value = "security.oauth2.enabled", havingValue = "true") public class OAuth2Configuration { + public static final String REDIRECT_URI_PATH = "{baseUrl}/login/oauth2/code/"; + private final ApplicationProperties applicationProperties; @Lazy private final UserService userService; @@ -62,13 +66,17 @@ public class OAuth2Configuration { private Optional googleClientRegistration() { OAUTH2 oauth = applicationProperties.getSecurity().getOauth2(); + 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( @@ -76,15 +84,13 @@ public class OAuth2Configuration { .clientId(google.getClientId()) .clientSecret(google.getClientSecret()) .scope(google.getScopes()) - .authorizationUri(google.getAuthorizationuri()) - .tokenUri(google.getTokenuri()) - .userInfoUri(google.getUserinfouri()) + .authorizationUri(google.getAuthorizationUri()) + .tokenUri(google.getTokenUri()) + .userInfoUri(google.getUserinfoUri()) .userNameAttributeName(google.getUseAsUsername()) .clientName(google.getClientName()) - .redirectUri("{baseUrl}/login/oauth2/code/" + google.getName()) - .authorizationGrantType( - org.springframework.security.oauth2.core - .AuthorizationGrantType.AUTHORIZATION_CODE) + .redirectUri(REDIRECT_URI_PATH + google.getName()) + .authorizationGrantType(AUTHORIZATION_CODE) .build()) : Optional.empty(); } @@ -113,36 +119,33 @@ public class OAuth2Configuration { } private Optional githubClientRegistration() { - OAUTH2 oauth = applicationProperties.getSecurity().getOauth2(); - if (oauth == null || !oauth.getEnabled()) { + if (isOauthOrClientEmpty()) { return Optional.empty(); } - Client client = oauth.getClient(); - if (client == null) { - return Optional.empty(); - } - GithubProvider github = client.getGithub(); + + GithubProvider github = + applicationProperties.getSecurity().getOauth2().getClient().getGithub(); + return github != null && github.isSettingsValid() ? Optional.of( ClientRegistration.withRegistrationId(github.getName()) .clientId(github.getClientId()) .clientSecret(github.getClientSecret()) .scope(github.getScopes()) - .authorizationUri(github.getAuthorizationuri()) - .tokenUri(github.getTokenuri()) - .userInfoUri(github.getUserinfouri()) + .authorizationUri(github.getAuthorizationUri()) + .tokenUri(github.getTokenUri()) + .userInfoUri(github.getUserinfoUri()) .userNameAttributeName(github.getUseAsUsername()) .clientName(github.getClientName()) - .redirectUri("{baseUrl}/login/oauth2/code/" + github.getName()) - .authorizationGrantType( - org.springframework.security.oauth2.core - .AuthorizationGrantType.AUTHORIZATION_CODE) + .redirectUri(REDIRECT_URI_PATH + github.getName()) + .authorizationGrantType(AUTHORIZATION_CODE) .build()) : Optional.empty(); } private Optional oidcClientRegistration() { OAUTH2 oauth = applicationProperties.getSecurity().getOauth2(); + if (oauth == null || oauth.getIssuer() == null || oauth.getIssuer().isEmpty() @@ -156,6 +159,7 @@ public class OAuth2Configuration { || oauth.getUseAsUsername().isEmpty()) { return Optional.empty(); } + return Optional.of( ClientRegistrations.fromIssuerLocation(oauth.getIssuer()) .registrationId("oidc") @@ -164,13 +168,23 @@ public class OAuth2Configuration { .scope(oauth.getScopes()) .userNameAttributeName(oauth.getUseAsUsername()) .clientName("OIDC") - .redirectUri("{baseUrl}/login/oauth2/code/oidc") - .authorizationGrantType( - org.springframework.security.oauth2.core.AuthorizationGrantType - .AUTHORIZATION_CODE) + .redirectUri(REDIRECT_URI_PATH + "oidc") + .authorizationGrantType(AUTHORIZATION_CODE) .build()); } + private boolean isOauthOrClientEmpty() { + OAUTH2 oauth = applicationProperties.getSecurity().getOauth2(); + + if (oauth == null || !oauth.getEnabled()) { + return false; + } + + Client client = oauth.getClient(); + + return client != null; + } + /* This following function is to grant Authorities to the OAUTH2 user from the values stored in the database. This is required for the internal; 'hasRole()' function to give out the correct role. diff --git a/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticationSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticationSuccessHandler.java index 0f8893239..1b697e1d6 100644 --- a/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticationSuccessHandler.java +++ b/src/main/java/stirling/software/SPDF/config/security/saml2/CustomSaml2AuthenticationSuccessHandler.java @@ -21,7 +21,7 @@ import stirling.software.SPDF.config.security.UserService; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2; import stirling.software.SPDF.model.AuthenticationType; -import stirling.software.SPDF.model.provider.UnsupportedProviderException; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; import stirling.software.SPDF.utils.RequestUriUtils; @AllArgsConstructor diff --git a/src/main/java/stirling/software/SPDF/controller/api/UserController.java b/src/main/java/stirling/software/SPDF/controller/api/UserController.java index 3e37a8ed8..b70e12eef 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/UserController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/UserController.java @@ -36,7 +36,7 @@ import stirling.software.SPDF.model.AuthenticationType; import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.User; import stirling.software.SPDF.model.api.user.UsernameAndPass; -import stirling.software.SPDF.model.provider.UnsupportedProviderException; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; @Controller @Tag(name = "User", description = "User APIs") diff --git a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java index 845ad675e..f7221367f 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java @@ -39,6 +39,7 @@ import stirling.software.SPDF.repository.UserRepository; @Tag(name = "Account Security", description = "Account Security APIs") public class AccountWebController { + public static final String OAUTH_2_AUTHORIZATION = "/oauth2/authorization/"; private final ApplicationProperties applicationProperties; private final SessionPersistentRegistry sessionPersistentRegistry; @@ -67,26 +68,24 @@ public class AccountWebController { if (oauth != null) { if (oauth.getEnabled()) { if (oauth.isSettingsValid()) { - providerList.put("/oauth2/authorization/oidc", oauth.getProvider()); + providerList.put(OAUTH_2_AUTHORIZATION + "oidc", oauth.getProvider()); } Client client = oauth.getClient(); if (client != null) { GoogleProvider google = client.getGoogle(); if (google.isSettingsValid()) { providerList.put( - "/oauth2/authorization/" + google.getName(), - google.getClientName()); + OAUTH_2_AUTHORIZATION + google.getName(), google.getClientName()); } GithubProvider github = client.getGithub(); if (github.isSettingsValid()) { providerList.put( - "/oauth2/authorization/" + github.getName(), - github.getClientName()); + OAUTH_2_AUTHORIZATION + github.getName(), github.getClientName()); } KeycloakProvider keycloak = client.getKeycloak(); if (keycloak.isSettingsValid()) { providerList.put( - "/oauth2/authorization/" + keycloak.getName(), + OAUTH_2_AUTHORIZATION + keycloak.getName(), keycloak.getClientName()); } } @@ -103,7 +102,7 @@ public class AccountWebController { .removeIf(entry -> entry.getKey() == null || entry.getValue() == null); model.addAttribute("providerlist", providerList); model.addAttribute("loginMethod", securityProps.getLoginMethod()); - boolean altLogin = providerList.size() > 0 ? securityProps.isAltLogin() : false; + boolean altLogin = !providerList.isEmpty() ? securityProps.isAltLogin() : false; model.addAttribute("altLogin", altLogin); model.addAttribute("currentPage", "login"); String error = request.getParameter("error"); diff --git a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java index 0400b3d8b..58272998d 100644 --- a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java +++ b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java @@ -34,10 +34,11 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.config.InstallationPathConfig; import stirling.software.SPDF.config.YamlPropertySourceFactory; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; 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.UnsupportedProviderException; +import stirling.software.SPDF.model.provider.Provider; @Configuration @ConfigurationProperties(prefix = "") @@ -230,7 +231,7 @@ public class ApplicationProperties { List scopesList = Arrays.stream(scopes.split(",")) .map(String::trim) - .collect(Collectors.toList()); + .toList(); this.scopes.addAll(scopesList); } @@ -257,18 +258,13 @@ public class ApplicationProperties { private KeycloakProvider keycloak = new KeycloakProvider(); public Provider get(String registrationId) throws UnsupportedProviderException { - switch (registrationId.toLowerCase()) { - case "google": - return getGoogle(); - case "github": - return getGithub(); - case "keycloak": - return getKeycloak(); - default: - throw new UnsupportedProviderException( - "Logout from the provider is not supported? Report it at" - + " https://github.com/Stirling-Tools/Stirling-PDF/issues"); - } + return switch (registrationId.toLowerCase()) { + case "google" -> getGoogle(); + case "github" -> getGithub(); + case "keycloak" -> getKeycloak(); + default -> throw new UnsupportedProviderException( + "Logout from the provider is not supported. Report it at https://github.com/Stirling-Tools/Stirling-PDF/issues"); + }; } } } @@ -335,10 +331,10 @@ public class ApplicationProperties { @Override public String toString() { return """ - Driver { - driverName='%s' - } - """ + Driver { + driverName='%s' + } + """ .formatted(driverName); } } diff --git a/src/main/java/stirling/software/SPDF/model/Provider.java b/src/main/java/stirling/software/SPDF/model/Provider.java deleted file mode 100644 index 87f5fa298..000000000 --- a/src/main/java/stirling/software/SPDF/model/Provider.java +++ /dev/null @@ -1,80 +0,0 @@ -package stirling.software.SPDF.model; - -import java.util.Collection; - -public class Provider implements ProviderInterface { - private String name; - private String clientName; - - public String getName() { - return name; - } - - public String getClientName() { - return clientName; - } - - protected boolean isValid(String value, String name) { - if (value != null && !value.trim().isEmpty()) { - return true; - } - return false; - } - - protected boolean isValid(Collection value, String name) { - if (value != null && !value.isEmpty()) { - return true; - } - return false; - } - - @Override - public Collection getScopes() { - throw new UnsupportedOperationException("Unimplemented method 'getScope'"); - } - - @Override - public void setScopes(String scopes) { - throw new UnsupportedOperationException("Unimplemented method 'setScope'"); - } - - @Override - public String getUseAsUsername() { - throw new UnsupportedOperationException("Unimplemented method 'getUseAsUsername'"); - } - - @Override - public void setUseAsUsername(String useAsUsername) { - throw new UnsupportedOperationException("Unimplemented method 'setUseAsUsername'"); - } - - @Override - public String getIssuer() { - throw new UnsupportedOperationException("Unimplemented method 'getIssuer'"); - } - - @Override - public void setIssuer(String issuer) { - throw new UnsupportedOperationException("Unimplemented method 'setIssuer'"); - } - - @Override - public String getClientSecret() { - throw new UnsupportedOperationException("Unimplemented method 'getClientSecret'"); - } - - @Override - public void setClientSecret(String clientSecret) { - throw new UnsupportedOperationException("Unimplemented method 'setClientSecret'"); - } - - @Override - public String getClientId() { - throw new UnsupportedOperationException("Unimplemented method 'getClientId'"); - } - - @Override - public void setClientId(String clientId) { - throw new UnsupportedOperationException("Unimplemented method 'setClientId'"); - } -} diff --git a/src/main/java/stirling/software/SPDF/model/ProviderInterface.java b/src/main/java/stirling/software/SPDF/model/ProviderInterface.java deleted file mode 100644 index d0d54827a..000000000 --- a/src/main/java/stirling/software/SPDF/model/ProviderInterface.java +++ /dev/null @@ -1,26 +0,0 @@ -package stirling.software.SPDF.model; - -import java.util.Collection; - -public interface ProviderInterface { - - public Collection 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); -} diff --git a/src/main/java/stirling/software/SPDF/model/provider/UnsupportedProviderException.java b/src/main/java/stirling/software/SPDF/model/exception/UnsupportedProviderException.java similarity index 76% rename from src/main/java/stirling/software/SPDF/model/provider/UnsupportedProviderException.java rename to src/main/java/stirling/software/SPDF/model/exception/UnsupportedProviderException.java index f90106015..d0bd8330c 100644 --- a/src/main/java/stirling/software/SPDF/model/provider/UnsupportedProviderException.java +++ b/src/main/java/stirling/software/SPDF/model/exception/UnsupportedProviderException.java @@ -1,4 +1,4 @@ -package stirling.software.SPDF.model.provider; +package stirling.software.SPDF.model.exception; public class UnsupportedProviderException extends Exception { public UnsupportedProviderException(String message) { diff --git a/src/main/java/stirling/software/SPDF/model/provider/GithubProvider.java b/src/main/java/stirling/software/SPDF/model/provider/GithubProvider.java index afe7fcb77..ff00eca54 100644 --- a/src/main/java/stirling/software/SPDF/model/provider/GithubProvider.java +++ b/src/main/java/stirling/software/SPDF/model/provider/GithubProvider.java @@ -1,60 +1,44 @@ package stirling.software.SPDF.model.provider; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.stream.Collectors; -import stirling.software.SPDF.model.Provider; +import lombok.NoArgsConstructor; +// @Setter +@NoArgsConstructor public 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"; + private static final String NAME = "github"; + private static final String CLIENT_NAME = "GitHub"; + private static final String AUTHORIZATION_URI = "https://github.com/login/oauth/authorize"; + 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 String clientId; private String clientSecret; private Collection scopes = new ArrayList<>(); private String useAsUsername = "login"; - public String getAuthorizationuri() { - return authorizationUri; - } - - public String getTokenuri() { - return tokenUri; - } - - public String getUserinfouri() { - return userInfoUri; - } - - @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) { + public GithubProvider( + String clientId, String clientSecret, Collection scopes, String useAsUsername) { + super(null, NAME, CLIENT_NAME, clientId, clientSecret, scopes, useAsUsername); this.clientId = clientId; - } - - @Override - public String getClientSecret() { - return this.clientSecret; - } - - @Override - public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; + this.scopes = scopes; + this.useAsUsername = useAsUsername; + } + + public String getAuthorizationUri() { + return AUTHORIZATION_URI; + } + + public String getTokenUri() { + return TOKEN_URI; + } + + public String getUserinfoUri() { + return USER_INFO_URI; } @Override @@ -66,22 +50,6 @@ public class GithubProvider extends Provider { 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=" @@ -94,21 +62,4 @@ public class GithubProvider extends Provider { + useAsUsername + "]"; } - - @Override - public String getName() { - return "github"; - } - - @Override - public String getClientName() { - 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"); - } } diff --git a/src/main/java/stirling/software/SPDF/model/provider/GoogleProvider.java b/src/main/java/stirling/software/SPDF/model/provider/GoogleProvider.java index e43e1327b..d1ecb5395 100644 --- a/src/main/java/stirling/software/SPDF/model/provider/GoogleProvider.java +++ b/src/main/java/stirling/software/SPDF/model/provider/GoogleProvider.java @@ -1,61 +1,45 @@ package stirling.software.SPDF.model.provider; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.stream.Collectors; -import stirling.software.SPDF.model.Provider; +import lombok.NoArgsConstructor; +// @Setter +@NoArgsConstructor public 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 = + private static final String NAME = "google"; + private static final String CLIENT_NAME = "Google"; + private static final String AUTHORIZATION_URI = "https://accounts.google.com/o/oauth2/v2/auth"; + private static final String TOKEN_URI = "https://www.googleapis.com/oauth2/v4/token"; + private static final String USER_INFO_URI = "https://www.googleapis.com/oauth2/v3/userinfo?alt=json"; + private String clientId; private String clientSecret; private Collection scopes = new ArrayList<>(); private String useAsUsername = "email"; - public String getAuthorizationuri() { - return authorizationUri; - } - - public String getTokenuri() { - return tokenUri; - } - - public String getUserinfouri() { - return userInfoUri; - } - - @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) { + public GoogleProvider( + String clientId, String clientSecret, Collection scopes, String useAsUsername) { + super(null, NAME, CLIENT_NAME, clientId, clientSecret, scopes, useAsUsername); this.clientId = clientId; - } - - @Override - public String getClientSecret() { - return this.clientSecret; - } - - @Override - public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; + this.scopes = scopes; + this.useAsUsername = useAsUsername; + } + + public String getAuthorizationUri() { + return AUTHORIZATION_URI; + } + + public String getTokenUri() { + return TOKEN_URI; + } + + public String getUserinfoUri() { + return USER_INFO_URI; } @Override @@ -68,22 +52,6 @@ public class GoogleProvider extends Provider { 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=" @@ -96,21 +64,4 @@ public class GoogleProvider extends Provider { + useAsUsername + "]"; } - - @Override - public String getName() { - return "google"; - } - - @Override - public String getClientName() { - 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"); - } } diff --git a/src/main/java/stirling/software/SPDF/model/provider/KeycloakProvider.java b/src/main/java/stirling/software/SPDF/model/provider/KeycloakProvider.java index d715b1038..77a50bdc3 100644 --- a/src/main/java/stirling/software/SPDF/model/provider/KeycloakProvider.java +++ b/src/main/java/stirling/software/SPDF/model/provider/KeycloakProvider.java @@ -1,76 +1,50 @@ package stirling.software.SPDF.model.provider; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.stream.Collectors; -import stirling.software.SPDF.model.Provider; +import lombok.NoArgsConstructor; +// @Setter +@NoArgsConstructor public class KeycloakProvider extends Provider { + private static final String NAME = "keycloak"; + private static final String CLIENT_NAME = "Keycloak"; + private String issuer; private String clientId; private String clientSecret; - private Collection scopes = new ArrayList<>(); + private Collection scopes; private String useAsUsername = "email"; - @Override - public String getIssuer() { - return this.issuer; - } - - @Override - public void setIssuer(String issuer) { + public KeycloakProvider( + String issuer, + String clientId, + String clientSecret, + Collection scopes, + String useAsUsername) { + super(issuer, NAME, CLIENT_NAME, clientId, clientSecret, scopes, useAsUsername); + this.useAsUsername = useAsUsername; 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; + this.scopes = scopes; } @Override public Collection getScopes() { + var scopes = super.getScopes(); + if (scopes == null || scopes.isEmpty()) { scopes = new ArrayList<>(); scopes.add("profile"); scopes.add("email"); } + 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 "Keycloak [issuer=" @@ -78,29 +52,11 @@ public class KeycloakProvider extends Provider { + ", clientId=" + clientId + ", clientSecret=" - + (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL") + + (clientSecret != null && !clientSecret.isBlank() ? "MASKED" : "NULL") + ", scopes=" + scopes + ", useAsUsername=" + useAsUsername + "]"; } - - @Override - public String getName() { - return "keycloak"; - } - - @Override - public String getClientName() { - 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"); - } } diff --git a/src/main/java/stirling/software/SPDF/model/provider/Provider.java b/src/main/java/stirling/software/SPDF/model/provider/Provider.java new file mode 100644 index 000000000..e2a638c0d --- /dev/null +++ b/src/main/java/stirling/software/SPDF/model/provider/Provider.java @@ -0,0 +1,84 @@ +package stirling.software.SPDF.model.provider; + +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Collectors; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public abstract class Provider { + + private String issuer; + private String name; + private String clientName; + private String clientId; + private String clientSecret; + private Collection scopes; + private String useAsUsername; + + public Provider( + String issuer, + String name, + String clientName, + String clientId, + String clientSecret, + Collection scopes, + String useAsUsername) { + this.issuer = issuer; + this.name = name; + this.clientName = clientName; + this.clientId = clientId; + this.clientSecret = clientSecret; + this.scopes = scopes; + this.useAsUsername = !useAsUsername.isBlank() ? useAsUsername : "email"; + } + + // todo: why are we passing name here if it's not used? + public boolean isSettingsValid() { + return isValid(this.getIssuer(), "issuer") + && isValid(this.getClientId(), "clientId") + && isValid(this.getClientSecret(), "clientSecret") + && isValid(this.getScopes(), "scopes") + && isValid(this.getUseAsUsername(), "useAsUsername"); + } + + private boolean isValid(String value, String name) { + return value != null && !value.isBlank(); + } + + private boolean isValid(Collection value, String name) { + return value != null && !value.isEmpty(); + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public void setName(String name) { + this.name = name; + } + + public void setClientName(String clientName) { + this.clientName = clientName; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public void setScopes(String scopes) { + this.scopes = + Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList()); + } + + public void setUseAsUsername(String useAsUsername) { + this.useAsUsername = useAsUsername; + } +} diff --git a/src/test/java/stirling/software/SPDF/config/security/database/DatabaseConfigTest.java b/src/test/java/stirling/software/SPDF/config/security/database/DatabaseConfigTest.java index bcfdc563c..6d933d721 100644 --- a/src/test/java/stirling/software/SPDF/config/security/database/DatabaseConfigTest.java +++ b/src/test/java/stirling/software/SPDF/config/security/database/DatabaseConfigTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import stirling.software.SPDF.model.ApplicationProperties; -import stirling.software.SPDF.model.provider.UnsupportedProviderException; +import stirling.software.SPDF.model.exception.UnsupportedProviderException; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock;