mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-02-12 00:15:51 +01:00
wip - working on saml2
This commit is contained in:
parent
7d784e2964
commit
f067e5df8c
@ -34,10 +34,7 @@ public class AppConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(
|
@ConditionalOnProperty(name = "system.customHTMLFiles", havingValue = "true")
|
||||||
name = "system.customHTMLFiles",
|
|
||||||
havingValue = "true",
|
|
||||||
matchIfMissing = false)
|
|
||||||
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
|
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
|
||||||
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
|
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
|
||||||
templateEngine.addTemplateResolver(new FileFallbackTemplateResolver(resourceLoader));
|
templateEngine.addTemplateResolver(new FileFallbackTemplateResolver(resourceLoader));
|
||||||
@ -137,8 +134,8 @@ public class AppConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ConditionalOnMissingClass("stirling.software.SPDF.config.security.SecurityConfiguration")
|
@ConditionalOnMissingClass("stirling.software.SPDF.config.security.SecurityConfiguration")
|
||||||
@Bean(name = "activSecurity")
|
@Bean(name = "activeSecurity")
|
||||||
public boolean missingActivSecurity() {
|
public boolean missingActiveSecurity() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ public class SecurityConfiguration {
|
|||||||
}
|
}
|
||||||
// Handle SAML
|
// Handle SAML
|
||||||
if (applicationProperties.getSecurity().isSaml2Active()) {
|
if (applicationProperties.getSecurity().isSaml2Active()) {
|
||||||
// && runningEE
|
// todo: && runningEE
|
||||||
// Configure the authentication provider
|
// Configure the authentication provider
|
||||||
OpenSaml4AuthenticationProvider authenticationProvider =
|
OpenSaml4AuthenticationProvider authenticationProvider =
|
||||||
new OpenSaml4AuthenticationProvider();
|
new OpenSaml4AuthenticationProvider();
|
||||||
@ -314,7 +314,7 @@ public class SecurityConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public boolean activSecurity() {
|
public boolean activeSecurity() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,9 +219,7 @@ public class OAuth2Configuration {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(
|
@ConditionalOnProperty(value = "security.oauth2.enabled", havingValue = "true")
|
||||||
value = "security.oauth2.enabled",
|
|
||||||
havingValue = "true")
|
|
||||||
GrantedAuthoritiesMapper userAuthoritiesMapper() {
|
GrantedAuthoritiesMapper userAuthoritiesMapper() {
|
||||||
return (authorities) -> {
|
return (authorities) -> {
|
||||||
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
|
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
|
||||||
|
@ -8,7 +8,6 @@ import org.springframework.security.saml2.core.Saml2Error;
|
|||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -21,7 +20,7 @@ public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthentica
|
|||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
AuthenticationException exception)
|
AuthenticationException exception)
|
||||||
throws IOException, ServletException {
|
throws IOException {
|
||||||
if (exception instanceof Saml2AuthenticationException) {
|
if (exception instanceof Saml2AuthenticationException) {
|
||||||
Saml2Error error = ((Saml2AuthenticationException) exception).getSaml2Error();
|
Saml2Error error = ((Saml2AuthenticationException) exception).getSaml2Error();
|
||||||
getRedirectStrategy()
|
getRedirectStrategy()
|
||||||
|
@ -140,19 +140,23 @@ public class AccountWebController {
|
|||||||
case "userAlreadyExistsWeb" -> errorOAuth = "userAlreadyExistsWebMessage";
|
case "userAlreadyExistsWeb" -> errorOAuth = "userAlreadyExistsWebMessage";
|
||||||
case "oAuth2AuthenticationErrorWeb" -> errorOAuth = "login.oauth2InvalidUserType";
|
case "oAuth2AuthenticationErrorWeb" -> errorOAuth = "login.oauth2InvalidUserType";
|
||||||
case "invalid_token_response" -> errorOAuth = "login.oauth2InvalidTokenResponse";
|
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 "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_request" -> errorOAuth = "login.oauth2invalidRequest";
|
||||||
case "invalid_id_token" -> errorOAuth = "login.oauth2InvalidIdToken";
|
case "invalid_id_token" -> errorOAuth = "login.oauth2InvalidIdToken";
|
||||||
case "oAuth2AdminBlockedUser" -> errorOAuth = "login.oAuth2AdminBlockedUser";
|
case "oAuth2AdminBlockedUser" -> errorOAuth = "login.oAuth2AdminBlockedUser";
|
||||||
case "userIsDisabled" -> errorOAuth = "login.userIsDisabled";
|
case "userIsDisabled" -> errorOAuth = "login.userIsDisabled";
|
||||||
case "invalid_destination" -> errorOAuth = "login.invalid_destination";
|
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
|
// Valid InResponseTo was not available from the validation context, unable to
|
||||||
// evaluate
|
// evaluate
|
||||||
case "invalid_in_response_to" -> errorOAuth = "login.invalid_in_response_to";
|
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);
|
model.addAttribute("errorOAuth", errorOAuth);
|
||||||
|
@ -109,7 +109,7 @@ public class ApplicationProperties {
|
|||||||
private int loginAttemptCount;
|
private int loginAttemptCount;
|
||||||
private long loginResetTimeMinutes;
|
private long loginResetTimeMinutes;
|
||||||
private String loginMethod = "all";
|
private String loginMethod = "all";
|
||||||
private String customGlobalAPIKey;
|
private String customGlobalAPIKey; // todo: expose?
|
||||||
|
|
||||||
public Boolean isAltLogin() {
|
public Boolean isAltLogin() {
|
||||||
return saml2.getEnabled() || oauth2.getEnabled();
|
return saml2.getEnabled() || oauth2.getEnabled();
|
||||||
|
@ -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
|
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
|
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)
|
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:
|
initialLogin:
|
||||||
username: '' # initial username for the first login
|
username: '' # initial username for the first login
|
||||||
password: '' # initial password for the first login
|
password: '' # initial password for the first login
|
||||||
@ -28,20 +29,20 @@ security:
|
|||||||
clientId: '' # client ID for Keycloak OAuth2
|
clientId: '' # client ID for Keycloak OAuth2
|
||||||
clientSecret: '' # client secret for Keycloak OAuth2
|
clientSecret: '' # client secret for Keycloak OAuth2
|
||||||
scopes: openid, profile, email # scopes 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:
|
google:
|
||||||
clientId: '' # client ID for Google OAuth2
|
clientId: '' # client ID for Google OAuth2
|
||||||
clientSecret: '' # client secret for Google OAuth2
|
clientSecret: '' # client secret for Google OAuth2
|
||||||
scopes: email, profile # scopes 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:
|
github:
|
||||||
clientId: '' # client ID for GitHub OAuth2
|
clientId: '' # client ID for GitHub OAuth2
|
||||||
clientSecret: '' # client secret for GitHub OAuth2
|
clientSecret: '' # client secret for GitHub OAuth2
|
||||||
scopes: read:user # scope for GitHub OAuth2
|
scopes: read:user # scope for GitHub OAuth2
|
||||||
useAsUsername: login # field to use as the username for GitHub OAuth2
|
useAsUsername: login # field to use as the username for GitHub OAuth2. Available options are: [email | login | name]
|
||||||
issuer: '' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint
|
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: '' # client ID from your provider
|
clientId: '5ibI9Ud5cRNFIcS1gIJME0shO6VZOy6Ae6XUrZL0' # client ID from your provider
|
||||||
clientSecret: '' # client secret from your provider
|
clientSecret: 'DFSD3B7MKLkWuEAasxxm2hghuzulPr37jdkrojPsGBz9MGwkfc' # client secret from your provider
|
||||||
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
|
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
|
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
|
useAsUsername: email # default is 'email'; custom fields can be used as the username
|
||||||
|
@ -253,11 +253,11 @@
|
|||||||
<label for="cacheInputs" th:text="#{settings.cacheInputs.name}"></label>
|
<label for="cacheInputs" th:text="#{settings.cacheInputs.name}"></label>
|
||||||
</div>
|
</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>
|
role="button" th:text="#{settings.accountSettings}" target="_blank">Account Settings</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<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>
|
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>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}"></button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,7 +29,6 @@ class ValidatorTest {
|
|||||||
when(provider.getClientId()).thenReturn("clientId");
|
when(provider.getClientId()).thenReturn("clientId");
|
||||||
when(provider.getClientSecret()).thenReturn("clientSecret");
|
when(provider.getClientSecret()).thenReturn("clientSecret");
|
||||||
when(provider.getScopes()).thenReturn(List.of("read:user"));
|
when(provider.getScopes()).thenReturn(List.of("read:user"));
|
||||||
when(provider.getUseAsUsername()).thenReturn(UsernameAttribute.EMAIL);
|
|
||||||
|
|
||||||
assertTrue(Validator.validateProvider(provider));
|
assertTrue(Validator.validateProvider(provider));
|
||||||
}
|
}
|
||||||
@ -44,15 +43,11 @@ class ValidatorTest {
|
|||||||
Provider generic = null;
|
Provider generic = null;
|
||||||
var google = new GoogleProvider(null, "clientSecret", List.of("scope"), UsernameAttribute.EMAIL);
|
var google = new GoogleProvider(null, "clientSecret", List.of("scope"), UsernameAttribute.EMAIL);
|
||||||
var github = new GitHubProvider("clientId", "", List.of("scope"), UsernameAttribute.LOGIN);
|
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(
|
return Stream.of(
|
||||||
Arguments.of(generic),
|
Arguments.of(generic),
|
||||||
Arguments.of(google),
|
Arguments.of(google),
|
||||||
Arguments.of(github),
|
Arguments.of(github)
|
||||||
Arguments.of(keycloak)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user