mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-04-26 01:17:19 +02:00
wip
This commit is contained in:
parent
ace34004f9
commit
7793be6949
@ -184,7 +184,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
|||||||
param = "error=badcredentials";
|
param = "error=badcredentials";
|
||||||
}
|
}
|
||||||
|
|
||||||
String redirect_url = UrlUtils.getOrigin(request) + "/login?" + param;
|
String redirectUrl = UrlUtils.getOrigin(request) + "/login?" + param;
|
||||||
|
|
||||||
// Redirect based on OAuth2 provider
|
// Redirect based on OAuth2 provider
|
||||||
switch (registrationId.toLowerCase()) {
|
switch (registrationId.toLowerCase()) {
|
||||||
@ -196,30 +196,30 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
|||||||
+ "?client_id="
|
+ "?client_id="
|
||||||
+ clientId
|
+ clientId
|
||||||
+ "&post_logout_redirect_uri="
|
+ "&post_logout_redirect_uri="
|
||||||
+ response.encodeRedirectURL(redirect_url);
|
+ response.encodeRedirectURL(redirectUrl);
|
||||||
log.info("Redirecting to Keycloak logout URL: {}", logoutUrl);
|
log.info("Redirecting to Keycloak logout URL: {}", logoutUrl);
|
||||||
response.sendRedirect(logoutUrl);
|
response.sendRedirect(logoutUrl);
|
||||||
}
|
}
|
||||||
case "github" -> {
|
case "github" -> {
|
||||||
// Add GitHub specific logout URL if needed
|
log.info(
|
||||||
// todo: why does the redirect go to github? shouldn't it come to Stirling PDF?
|
"No redirect URL for GitHub. Redirecting to default logout URL: {}",
|
||||||
String githubLogoutUrl = "https://github.com/logout";
|
redirectUrl);
|
||||||
log.info("Redirecting to GitHub logout URL: {}", redirect_url);
|
response.sendRedirect(redirectUrl);
|
||||||
response.sendRedirect(redirect_url);
|
|
||||||
}
|
}
|
||||||
case "google" -> {
|
case "google" -> {
|
||||||
// Add Google specific logout URL if needed
|
// Add Google specific logout URL if needed
|
||||||
// String googleLogoutUrl =
|
// 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(redirect_url);
|
// + response.encodeRedirectURL(redirectUrl);
|
||||||
log.info("Google does not have a specific logout URL");
|
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);
|
||||||
}
|
}
|
||||||
default -> {
|
default -> {
|
||||||
String defaultRedirectUrl = request.getContextPath() + "/login?" + param;
|
// String defaultRedirectUrl = request.getContextPath() + "/login?" +
|
||||||
log.info("Redirecting to default logout URL: {}", defaultRedirectUrl);
|
// param;
|
||||||
response.sendRedirect(defaultRedirectUrl);
|
log.info("Redirecting to default logout URL: {}", redirectUrl);
|
||||||
|
response.sendRedirect(redirectUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stirling.software.SPDF.config.security.oauth2;
|
package stirling.software.SPDF.config.security.oauth2;
|
||||||
|
|
||||||
import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE;
|
import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE;
|
||||||
|
import static stirling.software.SPDF.utils.validation.Validator.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -27,9 +28,7 @@ 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.Provider;
|
||||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
|
||||||
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Configuration
|
@Configuration
|
||||||
@ -68,10 +67,9 @@ public class OAuth2Configuration {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
GoogleProvider google =
|
Provider google = applicationProperties.getSecurity().getOauth2().getClient().getGoogle();
|
||||||
applicationProperties.getSecurity().getOauth2().getClient().getGoogle();
|
|
||||||
|
|
||||||
return google != null && google.isSettingsValid()
|
return validateSettings(google)
|
||||||
? Optional.of(
|
? Optional.of(
|
||||||
ClientRegistration.withRegistrationId(google.getName())
|
ClientRegistration.withRegistrationId(google.getName())
|
||||||
.clientId(google.getClientId())
|
.clientId(google.getClientId())
|
||||||
@ -79,7 +77,7 @@ public class OAuth2Configuration {
|
|||||||
.scope(google.getScopes())
|
.scope(google.getScopes())
|
||||||
.authorizationUri(google.getAuthorizationUri())
|
.authorizationUri(google.getAuthorizationUri())
|
||||||
.tokenUri(google.getTokenUri())
|
.tokenUri(google.getTokenUri())
|
||||||
.userInfoUri(google.getUserinfoUri())
|
.userInfoUri(google.getUserInfoUri())
|
||||||
.userNameAttributeName(google.getUseAsUsername())
|
.userNameAttributeName(google.getUseAsUsername())
|
||||||
.clientName(google.getClientName())
|
.clientName(google.getClientName())
|
||||||
.redirectUri(REDIRECT_URI_PATH + google.getName())
|
.redirectUri(REDIRECT_URI_PATH + google.getName())
|
||||||
@ -93,10 +91,10 @@ public class OAuth2Configuration {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
KeycloakProvider keycloak =
|
Provider keycloak =
|
||||||
applicationProperties.getSecurity().getOauth2().getClient().getKeycloak();
|
applicationProperties.getSecurity().getOauth2().getClient().getKeycloak();
|
||||||
|
|
||||||
return keycloak != null && keycloak.isSettingsValid()
|
return validateSettings(keycloak)
|
||||||
? Optional.of(
|
? Optional.of(
|
||||||
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
|
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
|
||||||
.registrationId(keycloak.getName())
|
.registrationId(keycloak.getName())
|
||||||
@ -114,10 +112,9 @@ public class OAuth2Configuration {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
GithubProvider github =
|
Provider github = applicationProperties.getSecurity().getOauth2().getClient().getGithub();
|
||||||
applicationProperties.getSecurity().getOauth2().getClient().getGithub();
|
|
||||||
|
|
||||||
return github != null && github.isSettingsValid()
|
return validateSettings(github)
|
||||||
? Optional.of(
|
? Optional.of(
|
||||||
ClientRegistration.withRegistrationId(github.getName())
|
ClientRegistration.withRegistrationId(github.getName())
|
||||||
.clientId(github.getClientId())
|
.clientId(github.getClientId())
|
||||||
@ -125,7 +122,7 @@ public class OAuth2Configuration {
|
|||||||
.scope(github.getScopes())
|
.scope(github.getScopes())
|
||||||
.authorizationUri(github.getAuthorizationUri())
|
.authorizationUri(github.getAuthorizationUri())
|
||||||
.tokenUri(github.getTokenUri())
|
.tokenUri(github.getTokenUri())
|
||||||
.userInfoUri(github.getUserinfoUri())
|
.userInfoUri(github.getUserInfoUri())
|
||||||
.userNameAttributeName(github.getUseAsUsername())
|
.userNameAttributeName(github.getUseAsUsername())
|
||||||
.clientName(github.getClientName())
|
.clientName(github.getClientName())
|
||||||
.redirectUri(REDIRECT_URI_PATH + github.getName())
|
.redirectUri(REDIRECT_URI_PATH + github.getName())
|
||||||
@ -180,6 +177,7 @@ public class OAuth2Configuration {
|
|||||||
This following function is to grant Authorities to the OAUTH2 user from the values stored in the database.
|
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.
|
This is required for the internal; 'hasRole()' function to give out the correct role.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(
|
@ConditionalOnProperty(
|
||||||
value = "security.oauth2.enabled",
|
value = "security.oauth2.enabled",
|
||||||
|
@ -24,24 +24,17 @@ import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
|||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ConditionalOnProperty(
|
@ConditionalOnProperty(value = "security.saml2.enabled", havingValue = "true")
|
||||||
value = "security.saml2.enabled",
|
|
||||||
havingValue = "true",
|
|
||||||
matchIfMissing = false)
|
|
||||||
public class SAML2Configuration {
|
public class SAML2Configuration {
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
public SAML2Configuration(ApplicationProperties applicationProperties) {
|
public SAML2Configuration(ApplicationProperties applicationProperties) {
|
||||||
|
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(
|
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
|
||||||
name = "security.saml2.enabled",
|
|
||||||
havingValue = "true",
|
|
||||||
matchIfMissing = false)
|
|
||||||
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
|
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
|
||||||
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
|
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
|
||||||
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
|
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
|
||||||
@ -71,10 +64,7 @@ public class SAML2Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(
|
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
|
||||||
name = "security.saml2.enabled",
|
|
||||||
havingValue = "true",
|
|
||||||
matchIfMissing = false)
|
|
||||||
public OpenSaml4AuthenticationRequestResolver authenticationRequestResolver(
|
public OpenSaml4AuthenticationRequestResolver authenticationRequestResolver(
|
||||||
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
|
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
|
||||||
OpenSaml4AuthenticationRequestResolver resolver =
|
OpenSaml4AuthenticationRequestResolver resolver =
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
|
import static stirling.software.SPDF.utils.validation.Validator.validateSettings;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -27,7 +29,7 @@ import stirling.software.SPDF.model.ApplicationProperties.Security;
|
|||||||
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.ApplicationProperties.Security.SAML2;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
||||||
import stirling.software.SPDF.model.provider.GithubProvider;
|
import stirling.software.SPDF.model.provider.GitHubProvider;
|
||||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||||
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
||||||
import stirling.software.SPDF.repository.UserRepository;
|
import stirling.software.SPDF.repository.UserRepository;
|
||||||
@ -60,28 +62,37 @@ public class AccountWebController {
|
|||||||
if (authentication != null && authentication.isAuthenticated()) {
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
return "redirect:/";
|
return "redirect:/";
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> providerList = new HashMap<>();
|
Map<String, String> providerList = new HashMap<>();
|
||||||
Security securityProps = applicationProperties.getSecurity();
|
Security securityProps = applicationProperties.getSecurity();
|
||||||
OAUTH2 oauth = securityProps.getOauth2();
|
OAUTH2 oauth = securityProps.getOauth2();
|
||||||
|
|
||||||
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());
|
providerList.put(OAUTH_2_AUTHORIZATION + "oidc", oauth.getProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
Client client = oauth.getClient();
|
Client client = oauth.getClient();
|
||||||
|
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
GoogleProvider google = client.getGoogle();
|
GoogleProvider google = client.getGoogle();
|
||||||
if (google.isSettingsValid()) {
|
|
||||||
|
if (validateSettings(google)) {
|
||||||
providerList.put(
|
providerList.put(
|
||||||
OAUTH_2_AUTHORIZATION + google.getName(), google.getClientName());
|
OAUTH_2_AUTHORIZATION + google.getName(), google.getClientName());
|
||||||
}
|
}
|
||||||
GithubProvider github = client.getGithub();
|
|
||||||
if (github.isSettingsValid()) {
|
GitHubProvider github = client.getGithub();
|
||||||
|
|
||||||
|
if (validateSettings(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 (keycloak.isSettingsValid()) {
|
|
||||||
|
if (validateSettings(keycloak)) {
|
||||||
providerList.put(
|
providerList.put(
|
||||||
OAUTH_2_AUTHORIZATION + keycloak.getName(),
|
OAUTH_2_AUTHORIZATION + keycloak.getName(),
|
||||||
keycloak.getClientName());
|
keycloak.getClientName());
|
||||||
@ -89,101 +100,74 @@ public class AccountWebController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SAML2 saml2 = securityProps.getSaml2();
|
SAML2 saml2 = securityProps.getSaml2();
|
||||||
|
|
||||||
if (securityProps.isSaml2Activ()
|
if (securityProps.isSaml2Activ()
|
||||||
&& applicationProperties.getSystem().getEnableAlphaFunctionality()) {
|
&& applicationProperties.getSystem().getEnableAlphaFunctionality()) {
|
||||||
providerList.put("/saml2/authenticate/" + saml2.getRegistrationId(), "SAML 2");
|
providerList.put("/saml2/authenticate/" + saml2.getRegistrationId(), "SAML 2");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any null keys/values from the providerList
|
// Remove any null keys/values from the providerList
|
||||||
providerList
|
providerList
|
||||||
.entrySet()
|
.entrySet()
|
||||||
.removeIf(entry -> entry.getKey() == null || entry.getValue() == null);
|
.removeIf(entry -> entry.getKey() == null || entry.getValue() == null);
|
||||||
model.addAttribute("providerlist", providerList);
|
model.addAttribute("providerlist", providerList);
|
||||||
model.addAttribute("loginMethod", securityProps.getLoginMethod());
|
model.addAttribute("loginMethod", securityProps.getLoginMethod());
|
||||||
|
|
||||||
boolean altLogin = !providerList.isEmpty() ? securityProps.isAltLogin() : false;
|
boolean altLogin = !providerList.isEmpty() ? securityProps.isAltLogin() : false;
|
||||||
|
|
||||||
model.addAttribute("altLogin", altLogin);
|
model.addAttribute("altLogin", altLogin);
|
||||||
model.addAttribute("currentPage", "login");
|
model.addAttribute("currentPage", "login");
|
||||||
String error = request.getParameter("error");
|
String error = request.getParameter("error");
|
||||||
|
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case "badcredentials":
|
case "badcredentials" -> error = "login.invalid";
|
||||||
error = "login.invalid";
|
case "locked" -> error = "login.locked";
|
||||||
break;
|
case "oauth2AuthenticationError" -> error = "userAlreadyExistsOAuthMessage";
|
||||||
case "locked":
|
|
||||||
error = "login.locked";
|
|
||||||
break;
|
|
||||||
case "oauth2AuthenticationError":
|
|
||||||
error = "userAlreadyExistsOAuthMessage";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model.addAttribute("error", error);
|
model.addAttribute("error", error);
|
||||||
}
|
}
|
||||||
String erroroauth = request.getParameter("erroroauth");
|
String erroroauth = request.getParameter("erroroauth");
|
||||||
if (erroroauth != null) {
|
if (erroroauth != null) {
|
||||||
switch (erroroauth) {
|
switch (erroroauth) {
|
||||||
case "oauth2AutoCreateDisabled":
|
case "oauth2AutoCreateDisabled" -> erroroauth = "login.oauth2AutoCreateDisabled";
|
||||||
erroroauth = "login.oauth2AutoCreateDisabled";
|
case "invalidUsername" -> erroroauth = "login.invalid";
|
||||||
break;
|
case "userAlreadyExistsWeb" -> erroroauth = "userAlreadyExistsWebMessage";
|
||||||
case "invalidUsername":
|
case "oauth2AuthenticationErrorWeb" -> erroroauth = "login.oauth2InvalidUserType";
|
||||||
erroroauth = "login.invalid";
|
case "invalid_token_response" -> erroroauth = "login.oauth2InvalidTokenResponse";
|
||||||
break;
|
case "authorization_request_not_found" ->
|
||||||
case "userAlreadyExistsWeb":
|
|
||||||
erroroauth = "userAlreadyExistsWebMessage";
|
|
||||||
break;
|
|
||||||
case "oauth2AuthenticationErrorWeb":
|
|
||||||
erroroauth = "login.oauth2InvalidUserType";
|
|
||||||
break;
|
|
||||||
case "invalid_token_response":
|
|
||||||
erroroauth = "login.oauth2InvalidTokenResponse";
|
|
||||||
break;
|
|
||||||
case "authorization_request_not_found":
|
|
||||||
erroroauth = "login.oauth2RequestNotFound";
|
erroroauth = "login.oauth2RequestNotFound";
|
||||||
break;
|
case "access_denied" -> erroroauth = "login.oauth2AccessDenied";
|
||||||
case "access_denied":
|
case "invalid_user_info_response" ->
|
||||||
erroroauth = "login.oauth2AccessDenied";
|
|
||||||
break;
|
|
||||||
case "invalid_user_info_response":
|
|
||||||
erroroauth = "login.oauth2InvalidUserInfoResponse";
|
erroroauth = "login.oauth2InvalidUserInfoResponse";
|
||||||
break;
|
case "invalid_request" -> erroroauth = "login.oauth2invalidRequest";
|
||||||
case "invalid_request":
|
case "invalid_id_token" -> erroroauth = "login.oauth2InvalidIdToken";
|
||||||
erroroauth = "login.oauth2invalidRequest";
|
case "oauth2_admin_blocked_user" -> erroroauth = "login.oauth2AdminBlockedUser";
|
||||||
break;
|
case "userIsDisabled" -> erroroauth = "login.userIsDisabled";
|
||||||
case "invalid_id_token":
|
case "invalid_destination" -> erroroauth = "login.invalid_destination";
|
||||||
erroroauth = "login.oauth2InvalidIdToken";
|
case "relying_party_registration_not_found" ->
|
||||||
break;
|
|
||||||
case "oauth2_admin_blocked_user":
|
|
||||||
erroroauth = "login.oauth2AdminBlockedUser";
|
|
||||||
break;
|
|
||||||
case "userIsDisabled":
|
|
||||||
erroroauth = "login.userIsDisabled";
|
|
||||||
break;
|
|
||||||
case "invalid_destination":
|
|
||||||
erroroauth = "login.invalid_destination";
|
|
||||||
break;
|
|
||||||
case "relying_party_registration_not_found":
|
|
||||||
erroroauth = "login.relyingPartyRegistrationNotFound";
|
erroroauth = "login.relyingPartyRegistrationNotFound";
|
||||||
break;
|
|
||||||
// Valid InResponseTo was not available from the validation context, unable to
|
// Valid InResponseTo was not available from the validation context, unable to
|
||||||
// evaluate
|
// evaluate
|
||||||
case "invalid_in_response_to":
|
case "invalid_in_response_to" -> erroroauth = "login.invalid_in_response_to";
|
||||||
erroroauth = "login.invalid_in_response_to";
|
case "not_authentication_provider_found" ->
|
||||||
break;
|
|
||||||
case "not_authentication_provider_found":
|
|
||||||
erroroauth = "login.not_authentication_provider_found";
|
erroroauth = "login.not_authentication_provider_found";
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model.addAttribute("erroroauth", erroroauth);
|
model.addAttribute("erroroauth", erroroauth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.getParameter("messageType") != null) {
|
if (request.getParameter("messageType") != null) {
|
||||||
model.addAttribute("messageType", "changedCredsMessage");
|
model.addAttribute("messageType", "changedCredsMessage");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.getParameter("logout") != null) {
|
if (request.getParameter("logout") != null) {
|
||||||
model.addAttribute("logoutMessage", "You have been logged out.");
|
model.addAttribute("logoutMessage", "You have been logged out.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return "login";
|
return "login";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
import stirling.software.SPDF.config.YamlPropertySourceFactory;
|
import stirling.software.SPDF.config.YamlPropertySourceFactory;
|
||||||
import stirling.software.SPDF.model.exception.UnsupportedProviderException;
|
import stirling.software.SPDF.model.exception.UnsupportedProviderException;
|
||||||
import stirling.software.SPDF.model.provider.GithubProvider;
|
import stirling.software.SPDF.model.provider.GitHubProvider;
|
||||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||||
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
||||||
import stirling.software.SPDF.model.provider.Provider;
|
import stirling.software.SPDF.model.provider.Provider;
|
||||||
@ -253,7 +253,7 @@ public class ApplicationProperties {
|
|||||||
@Data
|
@Data
|
||||||
public static class Client {
|
public static class Client {
|
||||||
private GoogleProvider google = new GoogleProvider();
|
private GoogleProvider google = new GoogleProvider();
|
||||||
private GithubProvider github = new GithubProvider();
|
private GitHubProvider github = new GitHubProvider();
|
||||||
private KeycloakProvider keycloak = new KeycloakProvider();
|
private KeycloakProvider keycloak = new KeycloakProvider();
|
||||||
|
|
||||||
public Provider get(String registrationId) throws UnsupportedProviderException {
|
public Provider get(String registrationId) throws UnsupportedProviderException {
|
||||||
|
@ -6,7 +6,7 @@ import java.util.Collection;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class GithubProvider extends Provider {
|
public class GitHubProvider extends Provider {
|
||||||
|
|
||||||
private static final String NAME = "github";
|
private static final String NAME = "github";
|
||||||
private static final String CLIENT_NAME = "GitHub";
|
private static final String CLIENT_NAME = "GitHub";
|
||||||
@ -14,51 +14,67 @@ 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";
|
||||||
|
|
||||||
private String clientId;
|
public GitHubProvider(String clientId, String clientSecret, String useAsUsername) {
|
||||||
private String clientSecret;
|
super(
|
||||||
private Collection<String> scopes = new ArrayList<>();
|
null,
|
||||||
private String useAsUsername = "login";
|
NAME,
|
||||||
|
CLIENT_NAME,
|
||||||
public GithubProvider(
|
clientId,
|
||||||
String clientId, String clientSecret, Collection<String> scopes, String useAsUsername) {
|
clientSecret,
|
||||||
super(null, NAME, CLIENT_NAME, clientId, clientSecret, scopes, useAsUsername);
|
new ArrayList<>(),
|
||||||
this.clientId = clientId;
|
useAsUsername != null ? useAsUsername : "login",
|
||||||
this.clientSecret = clientSecret;
|
AUTHORIZATION_URI,
|
||||||
this.scopes = scopes;
|
TOKEN_URI,
|
||||||
this.useAsUsername = useAsUsername;
|
USER_INFO_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getAuthorizationUri() {
|
public String getAuthorizationUri() {
|
||||||
return AUTHORIZATION_URI;
|
return AUTHORIZATION_URI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getTokenUri() {
|
public String getTokenUri() {
|
||||||
return TOKEN_URI;
|
return TOKEN_URI;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserinfoUri() {
|
@Override
|
||||||
|
public String getUserInfoUri() {
|
||||||
return USER_INFO_URI;
|
return USER_INFO_URI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClientName() {
|
||||||
|
return CLIENT_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> getScopes() {
|
public Collection<String> getScopes() {
|
||||||
|
Collection<String> scopes = super.getScopes();
|
||||||
|
|
||||||
if (scopes == null || scopes.isEmpty()) {
|
if (scopes == null || scopes.isEmpty()) {
|
||||||
scopes = new ArrayList<>();
|
scopes = new ArrayList<>();
|
||||||
scopes.add("read:user");
|
scopes.add("read:user");
|
||||||
}
|
}
|
||||||
|
|
||||||
return scopes;
|
return scopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "GitHub [clientId="
|
return "GitHub [clientId="
|
||||||
+ clientId
|
+ getClientId()
|
||||||
+ ", clientSecret="
|
+ ", clientSecret="
|
||||||
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
|
+ (getClientSecret() != null && !getClientSecret().isEmpty() ? "MASKED" : "NULL")
|
||||||
+ ", scopes="
|
+ ", scopes="
|
||||||
+ scopes
|
+ getScopes()
|
||||||
+ ", useAsUsername="
|
+ ", useAsUsername="
|
||||||
+ useAsUsername
|
+ getUseAsUsername()
|
||||||
+ "]";
|
+ "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,18 +15,18 @@ 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";
|
||||||
|
|
||||||
private String clientId;
|
public GoogleProvider(String clientId, String clientSecret, String useAsUsername) {
|
||||||
private String clientSecret;
|
super(
|
||||||
private Collection<String> scopes = new ArrayList<>();
|
null,
|
||||||
private String useAsUsername = "email";
|
NAME,
|
||||||
|
CLIENT_NAME,
|
||||||
public GoogleProvider(
|
clientId,
|
||||||
String clientId, String clientSecret, Collection<String> scopes, String useAsUsername) {
|
clientSecret,
|
||||||
super(null, NAME, CLIENT_NAME, clientId, clientSecret, scopes, useAsUsername);
|
new ArrayList<>(),
|
||||||
this.clientId = clientId;
|
useAsUsername,
|
||||||
this.clientSecret = clientSecret;
|
AUTHORIZATION_URI,
|
||||||
this.scopes = scopes;
|
TOKEN_URI,
|
||||||
this.useAsUsername = useAsUsername;
|
USER_INFO_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAuthorizationUri() {
|
public String getAuthorizationUri() {
|
||||||
@ -41,26 +41,39 @@ public class GoogleProvider extends Provider {
|
|||||||
return USER_INFO_URI;
|
return USER_INFO_URI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClientName() {
|
||||||
|
return CLIENT_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> getScopes() {
|
public Collection<String> getScopes() {
|
||||||
|
Collection<String> scopes = super.getScopes();
|
||||||
|
|
||||||
if (scopes == null || scopes.isEmpty()) {
|
if (scopes == null || scopes.isEmpty()) {
|
||||||
scopes = new ArrayList<>();
|
scopes = new ArrayList<>();
|
||||||
scopes.add("https://www.googleapis.com/auth/userinfo.email");
|
scopes.add("https://www.googleapis.com/auth/userinfo.email");
|
||||||
scopes.add("https://www.googleapis.com/auth/userinfo.profile");
|
scopes.add("https://www.googleapis.com/auth/userinfo.profile");
|
||||||
}
|
}
|
||||||
|
|
||||||
return scopes;
|
return scopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Google [clientId="
|
return "Google [clientId="
|
||||||
+ clientId
|
+ getClientId()
|
||||||
+ ", clientSecret="
|
+ ", clientSecret="
|
||||||
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
|
+ (getClientSecret() != null && !getClientSecret().isEmpty() ? "MASKED" : "NULL")
|
||||||
+ ", scopes="
|
+ ", scopes="
|
||||||
+ scopes
|
+ getScopes()
|
||||||
+ ", useAsUsername="
|
+ ", useAsUsername="
|
||||||
+ useAsUsername
|
+ getUseAsUsername()
|
||||||
+ "]";
|
+ "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,24 +11,29 @@ public class KeycloakProvider extends Provider {
|
|||||||
private static final String NAME = "keycloak";
|
private static final String NAME = "keycloak";
|
||||||
private static final String CLIENT_NAME = "Keycloak";
|
private static final String CLIENT_NAME = "Keycloak";
|
||||||
|
|
||||||
private String issuer;
|
|
||||||
private String clientId;
|
|
||||||
private String clientSecret;
|
|
||||||
private Collection<String> scopes;
|
|
||||||
private String useAsUsername = "email";
|
|
||||||
|
|
||||||
public KeycloakProvider(
|
public KeycloakProvider(
|
||||||
String issuer,
|
String issuer, String clientId, String clientSecret, String useAsUsername) {
|
||||||
String clientId,
|
super(
|
||||||
String clientSecret,
|
issuer,
|
||||||
Collection<String> scopes,
|
NAME,
|
||||||
String useAsUsername) {
|
CLIENT_NAME,
|
||||||
super(issuer, NAME, CLIENT_NAME, clientId, clientSecret, scopes, useAsUsername);
|
clientId,
|
||||||
this.useAsUsername = useAsUsername;
|
clientSecret,
|
||||||
this.issuer = issuer;
|
new ArrayList<>(),
|
||||||
this.clientId = clientId;
|
useAsUsername,
|
||||||
this.clientSecret = clientSecret;
|
null,
|
||||||
this.scopes = scopes;
|
null,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClientName() {
|
||||||
|
return CLIENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -47,15 +52,15 @@ public class KeycloakProvider extends Provider {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Keycloak [issuer="
|
return "Keycloak [issuer="
|
||||||
+ issuer
|
+ getIssuer()
|
||||||
+ ", clientId="
|
+ ", clientId="
|
||||||
+ clientId
|
+ getClientId()
|
||||||
+ ", clientSecret="
|
+ ", clientSecret="
|
||||||
+ (clientSecret != null && !clientSecret.isBlank() ? "MASKED" : "NULL")
|
+ (getClientSecret() != null && !getClientSecret().isBlank() ? "MASKED" : "NULL")
|
||||||
+ ", scopes="
|
+ ", scopes="
|
||||||
+ scopes
|
+ getScopes()
|
||||||
+ ", useAsUsername="
|
+ ", useAsUsername="
|
||||||
+ useAsUsername
|
+ getUseAsUsername()
|
||||||
+ "]";
|
+ "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,9 @@ public abstract class Provider {
|
|||||||
private String clientSecret;
|
private String clientSecret;
|
||||||
private Collection<String> scopes;
|
private Collection<String> scopes;
|
||||||
private String useAsUsername;
|
private String useAsUsername;
|
||||||
|
private String authorizationUri;
|
||||||
|
private String tokenUri;
|
||||||
|
private String userInfoUri;
|
||||||
|
|
||||||
public Provider(
|
public Provider(
|
||||||
String issuer,
|
String issuer,
|
||||||
@ -26,7 +29,10 @@ public abstract class Provider {
|
|||||||
String clientId,
|
String clientId,
|
||||||
String clientSecret,
|
String clientSecret,
|
||||||
Collection<String> scopes,
|
Collection<String> scopes,
|
||||||
String useAsUsername) {
|
String useAsUsername,
|
||||||
|
String authorizationUri,
|
||||||
|
String tokenUri,
|
||||||
|
String userInfoUri) {
|
||||||
this.issuer = issuer;
|
this.issuer = issuer;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.clientName = clientName;
|
this.clientName = clientName;
|
||||||
@ -34,28 +40,15 @@ public abstract class Provider {
|
|||||||
this.clientSecret = clientSecret;
|
this.clientSecret = clientSecret;
|
||||||
this.scopes = scopes;
|
this.scopes = scopes;
|
||||||
this.useAsUsername = !useAsUsername.isBlank() ? useAsUsername : "email";
|
this.useAsUsername = !useAsUsername.isBlank() ? useAsUsername : "email";
|
||||||
}
|
this.authorizationUri = authorizationUri;
|
||||||
|
this.tokenUri = tokenUri;
|
||||||
// todo: why are we passing name here if it's not used?
|
this.userInfoUri = userInfoUri;
|
||||||
// todo: use util class/method
|
|
||||||
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<String> value, String name) {
|
|
||||||
return value != null && !value.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setScopes(String scopes) {
|
public void setScopes(String scopes) {
|
||||||
|
if (scopes != null && !scopes.isBlank()) {
|
||||||
this.scopes =
|
this.scopes =
|
||||||
Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList());
|
Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package stirling.software.SPDF.utils.validation;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public class CollectionValidator implements Validator<Collection<String>> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean validate(Collection<String> input, String path) {
|
|
||||||
return input != null && !input.isEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package stirling.software.SPDF.utils.validation;
|
|
||||||
|
|
||||||
public class StringValidator implements Validator<String> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean validate(String input, String path) {
|
|
||||||
return input != null && !input.isBlank();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,36 @@
|
|||||||
package stirling.software.SPDF.utils.validation;
|
package stirling.software.SPDF.utils.validation;
|
||||||
|
|
||||||
public interface Validator<T> {
|
import java.util.Collection;
|
||||||
|
|
||||||
boolean validate(T input, String path);
|
import stirling.software.SPDF.model.provider.Provider;
|
||||||
|
|
||||||
|
public class Validator {
|
||||||
|
|
||||||
|
public static boolean validateSettings(Provider provider) {
|
||||||
|
if (provider == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStringEmpty(provider.getClientId())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStringEmpty(provider.getClientSecret())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCollectionEmpty(provider.getScopes())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isStringEmpty(provider.getUseAsUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isStringEmpty(String input) {
|
||||||
|
return input == null || input.isBlank();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCollectionEmpty(Collection<String> input) {
|
||||||
|
return input == null || input.isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package stirling.software.SPDF.utils.validation;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
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 java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class ValidatorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccessfulValidation() {
|
||||||
|
var provider = mock(GitHubProvider.class);
|
||||||
|
|
||||||
|
when(provider.getClientId()).thenReturn("clientId");
|
||||||
|
when(provider.getClientSecret()).thenReturn("clientSecret");
|
||||||
|
when(provider.getScopes()).thenReturn(List.of("read:user"));
|
||||||
|
when(provider.getUseAsUsername()).thenReturn("email");
|
||||||
|
|
||||||
|
assertTrue(Validator.validateSettings(provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("providerParams")
|
||||||
|
void testUnsuccessfulValidation(Provider provider) {
|
||||||
|
assertFalse(Validator.validateSettings(provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream<Arguments> providerParams() {
|
||||||
|
var generic = new GitHubProvider(null, "clientSecret", " ");
|
||||||
|
var google = new GoogleProvider(null, "clientSecret", "email");
|
||||||
|
var github = new GitHubProvider("clientId", "", "email");
|
||||||
|
var keycloak = new KeycloakProvider("issuer", "clientId", "clientSecret", " ");
|
||||||
|
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(generic),
|
||||||
|
Arguments.of(google),
|
||||||
|
Arguments.of(github),
|
||||||
|
Arguments.of(keycloak)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user