wip - working on saml2

This commit is contained in:
DarioGii 2025-02-01 23:30:28 +00:00 committed by Dario Ghunney Ware
parent 7d784e2964
commit f067e5df8c
9 changed files with 26 additions and 32 deletions

View File

@ -34,10 +34,7 @@ public class AppConfig {
}
@Bean
@ConditionalOnProperty(
name = "system.customHTMLFiles",
havingValue = "true",
matchIfMissing = false)
@ConditionalOnProperty(name = "system.customHTMLFiles", havingValue = "true")
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(new FileFallbackTemplateResolver(resourceLoader));
@ -137,8 +134,8 @@ public class AppConfig {
}
@ConditionalOnMissingClass("stirling.software.SPDF.config.security.SecurityConfiguration")
@Bean(name = "activSecurity")
public boolean missingActivSecurity() {
@Bean(name = "activeSecurity")
public boolean missingActiveSecurity() {
return false;
}

View File

@ -258,7 +258,7 @@ public class SecurityConfiguration {
}
// Handle SAML
if (applicationProperties.getSecurity().isSaml2Active()) {
// && runningEE
// todo: && runningEE
// Configure the authentication provider
OpenSaml4AuthenticationProvider authenticationProvider =
new OpenSaml4AuthenticationProvider();
@ -314,7 +314,7 @@ public class SecurityConfiguration {
}
@Bean
public boolean activSecurity() {
public boolean activeSecurity() {
return true;
}
}

View File

@ -219,9 +219,7 @@ public class OAuth2Configuration {
*/
@Bean
@ConditionalOnProperty(
value = "security.oauth2.enabled",
havingValue = "true")
@ConditionalOnProperty(value = "security.oauth2.enabled", havingValue = "true")
GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();

View File

@ -8,7 +8,6 @@ import org.springframework.security.saml2.core.Saml2Error;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@ -21,7 +20,7 @@ public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthentica
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
throws IOException {
if (exception instanceof Saml2AuthenticationException) {
Saml2Error error = ((Saml2AuthenticationException) exception).getSaml2Error();
getRedirectStrategy()

View File

@ -140,19 +140,23 @@ public class AccountWebController {
case "userAlreadyExistsWeb" -> errorOAuth = "userAlreadyExistsWebMessage";
case "oAuth2AuthenticationErrorWeb" -> errorOAuth = "login.oauth2InvalidUserType";
case "invalid_token_response" -> errorOAuth = "login.oauth2InvalidTokenResponse";
case "authorization_request_not_found" -> errorOAuth = "login.oauth2RequestNotFound";
case "authorization_request_not_found" ->
errorOAuth = "login.oauth2RequestNotFound";
case "access_denied" -> errorOAuth = "login.oauth2AccessDenied";
case "invalid_user_info_response" -> errorOAuth = "login.oauth2InvalidUserInfoResponse";
case "invalid_user_info_response" ->
errorOAuth = "login.oauth2InvalidUserInfoResponse";
case "invalid_request" -> errorOAuth = "login.oauth2invalidRequest";
case "invalid_id_token" -> errorOAuth = "login.oauth2InvalidIdToken";
case "oAuth2AdminBlockedUser" -> errorOAuth = "login.oAuth2AdminBlockedUser";
case "userIsDisabled" -> errorOAuth = "login.userIsDisabled";
case "invalid_destination" -> errorOAuth = "login.invalid_destination";
case "relying_party_registration_not_found" -> errorOAuth = "login.relyingPartyRegistrationNotFound";
case "relying_party_registration_not_found" ->
errorOAuth = "login.relyingPartyRegistrationNotFound";
// Valid InResponseTo was not available from the validation context, unable to
// evaluate
case "invalid_in_response_to" -> errorOAuth = "login.invalid_in_response_to";
case "not_authentication_provider_found" -> errorOAuth = "login.not_authentication_provider_found";
case "not_authentication_provider_found" ->
errorOAuth = "login.not_authentication_provider_found";
}
model.addAttribute("errorOAuth", errorOAuth);

View File

@ -109,7 +109,7 @@ public class ApplicationProperties {
private int loginAttemptCount;
private long loginResetTimeMinutes;
private String loginMethod = "all";
private String customGlobalAPIKey;
private String customGlobalAPIKey; // todo: expose?
public Boolean isAltLogin() {
return saml2.getEnabled() || oauth2.getEnabled();

View File

@ -17,6 +17,7 @@ security:
loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
loginMethod: saml2 # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2)
customGlobalAPIKey: 3R3T-WFPY-UNRW-LJFA-MMXM-YVJK-WCKY-PCRT # todo: this is in ApplicationProperties but not here. Should we add it
initialLogin:
username: '' # initial username for the first login
password: '' # initial password for the first login
@ -28,20 +29,20 @@ security:
clientId: '' # client ID for Keycloak OAuth2
clientSecret: '' # client secret for Keycloak OAuth2
scopes: openid, profile, email # scopes for Keycloak OAuth2
useAsUsername: preferred_username # field to use as the username for Keycloak OAuth2
useAsUsername: preferred_username # field to use as the username for Keycloak OAuth2. Available options are: [email | preferred_name]
google:
clientId: '' # client ID for Google OAuth2
clientSecret: '' # client secret for Google OAuth2
scopes: email, profile # scopes for Google OAuth2
useAsUsername: email # field to use as the username for Google OAuth2
useAsUsername: email # field to use as the username for Google OAuth2. Available options are: [email | name | given_name | family_name]
github:
clientId: '' # client ID for GitHub OAuth2
clientSecret: '' # client secret for GitHub OAuth2
scopes: read:user # scope for GitHub OAuth2
useAsUsername: login # field to use as the username for GitHub OAuth2
issuer: '' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint
clientId: '' # client ID from your provider
clientSecret: '' # client secret from your provider
useAsUsername: login # field to use as the username for GitHub OAuth2. Available options are: [email | login | name]
issuer: 'https://authentik.dev.stirlingpdf.com/application/o/stirlingpdf-oauth/' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint
clientId: '5ibI9Ud5cRNFIcS1gIJME0shO6VZOy6Ae6XUrZL0' # client ID from your provider
clientSecret: 'DFSD3B7MKLkWuEAasxxm2hghuzulPr37jdkrojPsGBz9MGwkfc' # client secret from your provider
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
useAsUsername: email # default is 'email'; custom fields can be used as the username

View File

@ -253,11 +253,11 @@
<label for="cacheInputs" th:text="#{settings.cacheInputs.name}"></label>
</div>
<a th:if="${@loginEnabled and @activSecurity}" th:href="@{'/account'}" class="btn btn-sm btn-outline-primary"
<a th:if="${@loginEnabled and @activeSecurity}" th:href="@{'/account'}" class="btn btn-sm btn-outline-primary"
role="button" th:text="#{settings.accountSettings}" target="_blank">Account Settings</a>
</div>
<div class="modal-footer">
<a th:if="${@loginEnabled and @activSecurity}" class="btn btn-danger" role="button"
<a th:if="${@loginEnabled and @activeSecurity}" class="btn btn-danger" role="button"
th:text="#{settings.signOut}" th:href="@{'/logout'}">Sign Out</a>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}"></button>
</div>

View File

@ -29,7 +29,6 @@ class ValidatorTest {
when(provider.getClientId()).thenReturn("clientId");
when(provider.getClientSecret()).thenReturn("clientSecret");
when(provider.getScopes()).thenReturn(List.of("read:user"));
when(provider.getUseAsUsername()).thenReturn(UsernameAttribute.EMAIL);
assertTrue(Validator.validateProvider(provider));
}
@ -44,15 +43,11 @@ class ValidatorTest {
Provider generic = null;
var google = new GoogleProvider(null, "clientSecret", List.of("scope"), UsernameAttribute.EMAIL);
var github = new GitHubProvider("clientId", "", List.of("scope"), UsernameAttribute.LOGIN);
var keycloak = new KeycloakProvider("issuer", "clientId", "clientSecret", List.of("scope"), UsernameAttribute.EMAIL);
keycloak.setUseAsUsername(null);
return Stream.of(
Arguments.of(generic),
Arguments.of(google),
Arguments.of(github),
Arguments.of(keycloak)
Arguments.of(github)
);
}