Adding JWTService and filter

This commit is contained in:
DarioGii 2025-07-04 10:38:35 +01:00 committed by Dario Ghunney Ware
parent 8fb78d612b
commit 98fb801247
13 changed files with 326 additions and 99 deletions

View File

@ -5,7 +5,11 @@
"Bash(mkdir:*)", "Bash(mkdir:*)",
"Bash(./gradlew:*)", "Bash(./gradlew:*)",
"Bash(grep:*)", "Bash(grep:*)",
"Bash(cat:*)" "Bash(cat:*)",
"Bash(find:*)",
"Bash(grep:*)",
"Bash(rg:*)",
"Bash(strings:*)"
], ],
"deny": [] "deny": []
} }

120
CLAUDE.md Normal file
View File

@ -0,0 +1,120 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Essential Development Commands
### Build and Run
```bash
# Build the project
./gradlew clean build
# Run locally (includes JWT authentication work-in-progress)
./gradlew bootRun
# Run specific module
./gradlew :stirling-pdf:bootRun
# Build with security features enabled/disabled
DISABLE_ADDITIONAL_FEATURES=false ./gradlew clean build # enable security
DISABLE_ADDITIONAL_FEATURES=true ./gradlew clean build # disable security
```
### Testing
```bash
# Run unit tests
./gradlew test
# Run comprehensive integration tests (builds all Docker versions and runs Cucumber tests)
./testing/test.sh
# Run Cucumber/BDD tests specifically
cd testing/cucumber && python -m behave
# Test web pages
cd testing && ./test_webpages.sh -f webpage_urls.txt -b http://localhost:8080
```
### Code Quality and Formatting
```bash
# Apply Java code formatting (required before commits)
./gradlew spotlessApply
# Check formatting compliance
./gradlew spotlessCheck
# Generate license report
./gradlew generateLicenseReport
```
### Docker Development
```bash
# Build different Docker variants
docker build --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest -f ./Dockerfile .
docker build --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite .
DISABLE_ADDITIONAL_FEATURES=false docker build --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-fat -f ./Dockerfile.fat .
# Use example Docker Compose configs
docker-compose -f exampleYmlFiles/docker-compose-latest-security.yml up -d
```
## Architecture Overview
Stirling-PDF is a Spring Boot web application for PDF manipulation with the following key architectural components:
### Multi-Module Structure
- **stirling-pdf/**: Main application module with web UI and REST APIs
- **common/**: Shared utilities and common functionality
- **proprietary/**: Enterprise/security features (JWT authentication, audit, teams)
### Technology Stack
- **Backend**: Spring Boot 3.5, Spring Security, Spring Data JPA
- **Frontend**: Thymeleaf templates, Bootstrap, vanilla JavaScript
- **PDF Processing**: Apache PDFBox 3.0, qpdf, LibreOffice
- **Authentication**: JWT-based stateless sessions (in development)
- **Database**: H2 (default), supports PostgreSQL/MySQL
- **Build**: Gradle with multi-project setup
### Current Development Context
The repository is on the `jwt-authentication` branch with work-in-progress changes to:
- JWT-based authentication system (`JWTService`, `JWTServiceInterface`)
- Stateless session management
- User model updates for JWT support
### Key Directories
- `stirling-pdf/src/main/java/stirling/software/SPDF/`: Main application code
- `controller/`: REST API endpoints and UI controllers
- `service/`: Business logic layer
- `config/`: Spring configuration classes
- `security/`: Authentication and authorization
- `stirling-pdf/src/main/resources/templates/`: Thymeleaf HTML templates
- `stirling-pdf/src/main/resources/static/`: CSS, JavaScript, and assets
- `proprietary/src/main/java/stirling/software/proprietary/`: Enterprise features
- `testing/`: Integration tests and Cucumber features
### Configuration Management
- Environment variables or `settings.yml` for runtime configuration
- Conditional feature compilation based on `DISABLE_ADDITIONAL_FEATURES`
- Multi-environment Docker configurations in `exampleYmlFiles/`
### API Design Patterns
- RESTful endpoints under `/api/v1/`
- OpenAPI/Swagger documentation available at `/swagger-ui/index.html`
- File upload/download handling with multipart form data
- Consistent error handling and response formats
## Development Workflow
1. **Environment Setup**: Set `DISABLE_ADDITIONAL_FEATURES=false` for full feature development
2. **Code Formatting**: Always run `./gradlew spotlessApply` before committing
3. **Testing Strategy**: Use `./testing/test.sh` for comprehensive testing before PRs
4. **Feature Development**: Follow the controller -> service -> template pattern
5. **Security**: JWT authentication is currently in development on this branch
## Important Notes
- The application supports conditional compilation of security features
- Translation files are in `messages_*.properties` format
- PDF processing operations are primarily stateless
- Docker is the recommended deployment method
- All text should be internationalized using translation keys

View File

@ -115,13 +115,14 @@ public class ApplicationProperties {
private InitialLogin initialLogin = new InitialLogin(); private InitialLogin initialLogin = new InitialLogin();
private OAUTH2 oauth2 = new OAUTH2(); private OAUTH2 oauth2 = new OAUTH2();
private SAML2 saml2 = new SAML2(); private SAML2 saml2 = new SAML2();
private JWT jwt = new JWT();
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;
public Boolean isAltLogin() { public Boolean isAltLogin() {
return saml2.getEnabled() || oauth2.getEnabled(); return saml2.getEnabled() || oauth2.getEnabled() || jwt.getEnabled();
} }
public enum LoginMethods { public enum LoginMethods {
@ -159,6 +160,10 @@ public class ApplicationProperties {
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString())); && !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
} }
public boolean isJwtActive() {
return (jwt != null && jwt.getEnabled());
}
@Data @Data
public static class InitialLogin { public static class InitialLogin {
private String username; private String username;
@ -297,6 +302,26 @@ public class ApplicationProperties {
} }
} }
} }
@Data
public static class JWT {
private Boolean enabled = false;
@ToString.Exclude private String secretKey;
private Long expiration = 3600000L; // Default 1 hour in milliseconds
private String algorithm = "HS256"; // Default HMAC algorithm
private String issuer = "Stirling-PDF"; // Default issuer
private Boolean enableRefreshToken = false;
private Long refreshTokenExpiration = 86400000L; // Default 24 hours
public boolean isSettingsValid() {
return enabled != null
&& enabled
&& secretKey != null
&& !secretKey.trim().isEmpty()
&& expiration != null
&& expiration > 0;
}
}
} }
@Data @Data

View File

@ -5,7 +5,7 @@ logging.level.org.eclipse.jetty=WARN
#logging.level.org.springframework.security.saml2=TRACE #logging.level.org.springframework.security.saml2=TRACE
#logging.level.org.springframework.security=DEBUG #logging.level.org.springframework.security=DEBUG
#logging.level.org.opensaml=DEBUG #logging.level.org.opensaml=DEBUG
#logging.level.stirling.software.SPDF.config.security: DEBUG #logging.level.stirling.software.proprietary.security: DEBUG
logging.level.com.zaxxer.hikari=WARN logging.level.com.zaxxer.hikari=WARN
spring.jpa.open-in-view=false spring.jpa.open-in-view=false
server.forward-headers-strategy=NATIVE server.forward-headers-strategy=NATIVE
@ -47,4 +47,4 @@ posthog.host=https://eu.i.posthog.com
spring.main.allow-bean-definition-overriding=true spring.main.allow-bean-definition-overriding=true
# Set up a consistent temporary directory location # Set up a consistent temporary directory location
java.io.tmpdir=${stirling.tempfiles.directory:${java.io.tmpdir}/stirling-pdf} java.io.tmpdir=${stirling.tempfiles.directory:${java.io.tmpdir}/stirling-pdf}

View File

@ -59,6 +59,14 @@ security:
idpCert: classpath:okta.cert # The certificate your Provider will use to authenticate your app's SAML authentication requests. Provided by your Provider idpCert: classpath:okta.cert # The certificate your Provider will use to authenticate your app's SAML authentication requests. Provided by your Provider
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
jwt:
enabled: true # set to 'true' to enable JWT authentication
secretKey: 'Uz4BgfMySCz2Uplhp1x9ij19vVV2bXYktROtrlw3CC0=' # secret
expiration: 3600000 # Expiration time in milliseconds. Default is 1 hour (3600000 ms)
algorithm: HS256 # JWT signing algorithm. Default is HS256
issuer: Stirling-PDF # Issuer of the JWT token. Default is 'Stirling-PDF'
refreshTokenEnabled: false # Set to 'true' to enable refresh tokens
refreshTokenExpiration: 86400000 # Expiration time for refresh tokens in milliseconds. Default is 1 day (86400000 ms)
premium: premium:
key: 00000000-0000-0000-0000-000000000000 key: 00000000-0000-0000-0000-000000000000

View File

@ -1,9 +1,15 @@
repositories { repositories {
maven { url = "https://build.shibboleth.net/maven/releases" } maven { url = "https://build.shibboleth.net/maven/releases" }
} }
ext {
jwtVersion = '0.12.6'
}
bootRun { bootRun {
enabled = false enabled = false
} }
spotless { spotless {
java { java {
target sourceSets.main.allJava target sourceSets.main.allJava
@ -38,6 +44,11 @@ dependencies {
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE'
api 'io.micrometer:micrometer-registry-prometheus' api 'io.micrometer:micrometer-registry-prometheus'
implementation 'com.unboundid.product.scim2:scim2-sdk-client:4.0.0' implementation 'com.unboundid.product.scim2:scim2-sdk-client:4.0.0'
// JWT dependencies
api "io.jsonwebtoken:jjwt-api:$jwtVersion"
runtimeOnly "io.jsonwebtoken:jjwt-impl:$jwtVersion"
runtimeOnly "io.jsonwebtoken:jjwt-jackson:$jwtVersion"
runtimeOnly 'com.h2database:h2:2.3.232' // Don't upgrade h2database runtimeOnly 'com.h2database:h2:2.3.232' // Don't upgrade h2database
runtimeOnly 'org.postgresql:postgresql:42.7.7' runtimeOnly 'org.postgresql:postgresql:42.7.7'
constraints { constraints {

View File

@ -17,6 +17,7 @@ import stirling.software.common.util.RequestUriUtils;
import stirling.software.proprietary.audit.AuditEventType; import stirling.software.proprietary.audit.AuditEventType;
import stirling.software.proprietary.audit.AuditLevel; import stirling.software.proprietary.audit.AuditLevel;
import stirling.software.proprietary.audit.Audited; import stirling.software.proprietary.audit.Audited;
import stirling.software.proprietary.security.service.JWTServiceInterface;
import stirling.software.proprietary.security.service.LoginAttemptService; import stirling.software.proprietary.security.service.LoginAttemptService;
import stirling.software.proprietary.security.service.UserService; import stirling.software.proprietary.security.service.UserService;
@ -24,13 +25,17 @@ import stirling.software.proprietary.security.service.UserService;
public class CustomAuthenticationSuccessHandler public class CustomAuthenticationSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler { extends SavedRequestAwareAuthenticationSuccessHandler {
private LoginAttemptService loginAttemptService; private final LoginAttemptService loginAttemptService;
private UserService userService; private final UserService userService;
private final JWTServiceInterface jwtService;
public CustomAuthenticationSuccessHandler( public CustomAuthenticationSuccessHandler(
LoginAttemptService loginAttemptService, UserService userService) { LoginAttemptService loginAttemptService,
UserService userService,
JWTServiceInterface jwtService) {
this.loginAttemptService = loginAttemptService; this.loginAttemptService = loginAttemptService;
this.userService = userService; this.userService = userService;
this.jwtService = jwtService;
} }
@Override @Override
@ -46,23 +51,35 @@ public class CustomAuthenticationSuccessHandler
} }
loginAttemptService.loginSucceeded(userName); loginAttemptService.loginSucceeded(userName);
// Get the saved request // Generate JWT token if JWT authentication is enabled
HttpSession session = request.getSession(false); boolean jwtEnabled = jwtService.isJwtEnabled();
SavedRequest savedRequest = if (jwtService != null && jwtEnabled) {
(session != null) try {
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST") String jwt = jwtService.generateToken(authentication);
: null; jwtService.addTokenToResponse(response, jwt);
log.debug("JWT token generated and added to response for user: {}", userName);
if (savedRequest != null } catch (Exception e) {
&& !RequestUriUtils.isStaticResource( log.error("Failed to generate JWT token for user: {}", userName, e);
request.getContextPath(), savedRequest.getRedirectUrl())) { }
// Redirect to the original destination
super.onAuthenticationSuccess(request, response, authentication);
} else {
// Redirect to the root URL (considering context path)
getRedirectStrategy().sendRedirect(request, response, "/");
} }
// super.onAuthenticationSuccess(request, response, authentication); if (jwtEnabled) {
// JWT mode: stateless authentication, redirect after setting token
getRedirectStrategy().sendRedirect(request, response, "/");
} else {
// Get the saved request
HttpSession session = request.getSession(false);
SavedRequest savedRequest =
(session != null)
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
: null;
if (savedRequest != null
&& !RequestUriUtils.isStaticResource(
request.getContextPath(), savedRequest.getRedirectUrl())) {
// Redirect to the original destination
super.onAuthenticationSuccess(request, response, authentication);
}
}
} }
} }

View File

@ -33,6 +33,7 @@ import stirling.software.proprietary.audit.AuditLevel;
import stirling.software.proprietary.audit.Audited; import stirling.software.proprietary.audit.Audited;
import stirling.software.proprietary.security.saml2.CertificateUtils; import stirling.software.proprietary.security.saml2.CertificateUtils;
import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal; import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.proprietary.security.service.JWTServiceInterface;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@ -40,15 +41,29 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
public static final String LOGOUT_PATH = "/login?logout=true"; public static final String LOGOUT_PATH = "/login?logout=true";
private final ApplicationProperties applicationProperties; private final ApplicationProperties.Security securityProperties;
private final AppConfig appConfig; private final AppConfig appConfig;
private final JWTServiceInterface jwtService;
@Override @Override
@Audited(type = AuditEventType.USER_LOGOUT, level = AuditLevel.BASIC) @Audited(type = AuditEventType.USER_LOGOUT, level = AuditLevel.BASIC)
public void onLogoutSuccess( public void onLogoutSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication) HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException { throws IOException {
// Clear JWT token if JWT authentication is enabled
if (jwtService != null && jwtService.isJwtEnabled()) {
try {
jwtService.clearTokenFromResponse(response);
log.debug("JWT token cleared from response during logout");
} catch (Exception e) {
log.error("Failed to clear JWT token during logout", e);
// Continue with normal logout flow even if JWT clearing fails
}
}
if (!response.isCommitted()) { if (!response.isCommitted()) {
if (authentication != null) { if (authentication != null) {
if (authentication instanceof Saml2Authentication samlAuthentication) { if (authentication instanceof Saml2Authentication samlAuthentication) {
@ -82,7 +97,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
Saml2Authentication samlAuthentication) Saml2Authentication samlAuthentication)
throws IOException { throws IOException {
SAML2 samlConf = applicationProperties.getSecurity().getSaml2(); SAML2 samlConf = securityProperties.getSaml2();
String registrationId = samlConf.getRegistrationId(); String registrationId = samlConf.getRegistrationId();
CustomSaml2AuthenticatedPrincipal principal = CustomSaml2AuthenticatedPrincipal principal =
@ -127,7 +142,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
OAuth2AuthenticationToken oAuthToken) OAuth2AuthenticationToken oAuthToken)
throws IOException { throws IOException {
String registrationId; String registrationId;
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2(); OAUTH2 oauth = securityProperties.getOauth2();
String path = checkForErrors(request); String path = checkForErrors(request);
String redirectUrl = UrlUtils.getOrigin(request) + "/login?" + path; String redirectUrl = UrlUtils.getOrigin(request) + "/login?" + path;

View File

@ -8,11 +8,14 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@ -39,6 +42,7 @@ import stirling.software.proprietary.security.database.repository.JPATokenReposi
import stirling.software.proprietary.security.database.repository.PersistentLoginRepository; import stirling.software.proprietary.security.database.repository.PersistentLoginRepository;
import stirling.software.proprietary.security.filter.FirstLoginFilter; import stirling.software.proprietary.security.filter.FirstLoginFilter;
import stirling.software.proprietary.security.filter.IPRateLimitingFilter; import stirling.software.proprietary.security.filter.IPRateLimitingFilter;
import stirling.software.proprietary.security.filter.JWTAuthenticationFilter;
import stirling.software.proprietary.security.filter.UserAuthenticationFilter; import stirling.software.proprietary.security.filter.UserAuthenticationFilter;
import stirling.software.proprietary.security.model.User; import stirling.software.proprietary.security.model.User;
import stirling.software.proprietary.security.oauth2.CustomOAuth2AuthenticationFailureHandler; import stirling.software.proprietary.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
@ -48,6 +52,7 @@ import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticationSuc
import stirling.software.proprietary.security.saml2.CustomSaml2ResponseAuthenticationConverter; import stirling.software.proprietary.security.saml2.CustomSaml2ResponseAuthenticationConverter;
import stirling.software.proprietary.security.service.CustomOAuth2UserService; import stirling.software.proprietary.security.service.CustomOAuth2UserService;
import stirling.software.proprietary.security.service.CustomUserDetailsService; import stirling.software.proprietary.security.service.CustomUserDetailsService;
import stirling.software.proprietary.security.service.JWTServiceInterface;
import stirling.software.proprietary.security.service.LoginAttemptService; import stirling.software.proprietary.security.service.LoginAttemptService;
import stirling.software.proprietary.security.service.UserService; import stirling.software.proprietary.security.service.UserService;
import stirling.software.proprietary.security.session.SessionPersistentRegistry; import stirling.software.proprietary.security.session.SessionPersistentRegistry;
@ -64,9 +69,11 @@ public class SecurityConfiguration {
private final boolean loginEnabledValue; private final boolean loginEnabledValue;
private final boolean runningProOrHigher; private final boolean runningProOrHigher;
private final ApplicationProperties applicationProperties; private final ApplicationProperties.Security securityProperties;
private final AppConfig appConfig; private final AppConfig appConfig;
private final UserAuthenticationFilter userAuthenticationFilter; private final UserAuthenticationFilter userAuthenticationFilter;
private final JWTAuthenticationFilter jwtAuthenticationFilter;
private final JWTServiceInterface jwtService;
private final LoginAttemptService loginAttemptService; private final LoginAttemptService loginAttemptService;
private final FirstLoginFilter firstLoginFilter; private final FirstLoginFilter firstLoginFilter;
private final SessionPersistentRegistry sessionRegistry; private final SessionPersistentRegistry sessionRegistry;
@ -82,8 +89,10 @@ public class SecurityConfiguration {
@Qualifier("loginEnabled") boolean loginEnabledValue, @Qualifier("loginEnabled") boolean loginEnabledValue,
@Qualifier("runningProOrHigher") boolean runningProOrHigher, @Qualifier("runningProOrHigher") boolean runningProOrHigher,
AppConfig appConfig, AppConfig appConfig,
ApplicationProperties applicationProperties, ApplicationProperties.Security securityProperties,
UserAuthenticationFilter userAuthenticationFilter, UserAuthenticationFilter userAuthenticationFilter,
JWTAuthenticationFilter jwtAuthenticationFilter,
JWTServiceInterface jwtService,
LoginAttemptService loginAttemptService, LoginAttemptService loginAttemptService,
FirstLoginFilter firstLoginFilter, FirstLoginFilter firstLoginFilter,
SessionPersistentRegistry sessionRegistry, SessionPersistentRegistry sessionRegistry,
@ -97,8 +106,10 @@ public class SecurityConfiguration {
this.loginEnabledValue = loginEnabledValue; this.loginEnabledValue = loginEnabledValue;
this.runningProOrHigher = runningProOrHigher; this.runningProOrHigher = runningProOrHigher;
this.appConfig = appConfig; this.appConfig = appConfig;
this.applicationProperties = applicationProperties; this.securityProperties = securityProperties;
this.userAuthenticationFilter = userAuthenticationFilter; this.userAuthenticationFilter = userAuthenticationFilter;
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
this.jwtService = jwtService;
this.loginAttemptService = loginAttemptService; this.loginAttemptService = loginAttemptService;
this.firstLoginFilter = firstLoginFilter; this.firstLoginFilter = firstLoginFilter;
this.sessionRegistry = sessionRegistry; this.sessionRegistry = sessionRegistry;
@ -115,14 +126,27 @@ public class SecurityConfiguration {
@Bean @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
if (applicationProperties.getSecurity().getCsrfDisabled() || !loginEnabledValue) { boolean jwtEnabled = securityProperties.isJwtActive();
http.csrf(csrf -> csrf.disable());
// Disable CSRF if explicitly disabled, login is disabled, or JWT is enabled (stateless)
if (securityProperties.getCsrfDisabled() || !loginEnabledValue || jwtEnabled) {
http.csrf(CsrfConfigurer::disable);
} }
if (loginEnabledValue) { if (loginEnabledValue) {
http.addFilterBefore( if (jwtEnabled && jwtAuthenticationFilter != null) {
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(
if (!applicationProperties.getSecurity().getCsrfDisabled()) { jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
// .addFilterAfter(
// jwtAuthenticationFilter,
// userAuthenticationFilter.getClass());
} else {
http.addFilterBefore(
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(rateLimitingFilter(), firstLoginFilter.getClass());
if (!securityProperties.getCsrfDisabled()) {
CookieCsrfTokenRepository cookieRepo = CookieCsrfTokenRepository cookieRepo =
CookieCsrfTokenRepository.withHttpOnlyFalse(); CookieCsrfTokenRepository.withHttpOnlyFalse();
CsrfTokenRequestAttributeHandler requestHandler = CsrfTokenRequestAttributeHandler requestHandler =
@ -156,18 +180,25 @@ public class SecurityConfiguration {
.csrfTokenRepository(cookieRepo) .csrfTokenRepository(cookieRepo)
.csrfTokenRequestHandler(requestHandler)); .csrfTokenRequestHandler(requestHandler));
} }
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class); // Configure session management based on JWT setting
http.sessionManagement( http.sessionManagement(
sessionManagement -> sessionManagement -> {
if (jwtEnabled) {
sessionManagement.sessionCreationPolicy(
SessionCreationPolicy.STATELESS);
} else {
sessionManagement sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(10) .maximumSessions(10)
.maxSessionsPreventsLogin(false) .maxSessionsPreventsLogin(false)
.sessionRegistry(sessionRegistry) .sessionRegistry(sessionRegistry)
.expiredUrl("/login?logout=true")); .expiredUrl("/login?logout=true");
}
});
http.authenticationProvider(daoAuthenticationProvider()); http.authenticationProvider(daoAuthenticationProvider());
http.requestCache(requestCache -> requestCache.requestCache(new NullRequestCache())); http.requestCache(requestCache -> requestCache.requestCache(new NullRequestCache()));
// Configure logout behavior based on JWT setting
http.logout( http.logout(
logout -> logout ->
logout.logoutRequestMatcher( logout.logoutRequestMatcher(
@ -175,31 +206,36 @@ public class SecurityConfiguration {
.matcher("/logout")) .matcher("/logout"))
.logoutSuccessHandler( .logoutSuccessHandler(
new CustomLogoutSuccessHandler( new CustomLogoutSuccessHandler(
applicationProperties, appConfig)) securityProperties, appConfig, jwtService))
.clearAuthentication(true) .clearAuthentication(true)
.invalidateHttpSession(true) .invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "remember-me")); .deleteCookies(
http.rememberMe( "JSESSIONID", "remember-me", "STIRLING_JWT_TOKEN"));
rememberMeConfigurer -> // Use the configurator directly // Only configure remember-me if JWT is not enabled (stateless) todo: check if remember-me can be used with JWT
rememberMeConfigurer if (!jwtEnabled) {
.tokenRepository(persistentTokenRepository()) http.rememberMe(
.tokenValiditySeconds( // 14 days rememberMeConfigurer -> // Use the configurator directly
14 * 24 * 60 * 60) rememberMeConfigurer
.userDetailsService( // Your existing UserDetailsService .tokenRepository(persistentTokenRepository())
userDetailsService) .tokenValiditySeconds( // 14 days
.useSecureCookie( // Enable secure cookie 14 * 24 * 60 * 60)
true) .userDetailsService( // Your existing UserDetailsService
.rememberMeParameter( // Form parameter name userDetailsService)
"remember-me") .useSecureCookie( // Enable secure cookie
.rememberMeCookieName( // Cookie name true)
"remember-me") .rememberMeParameter( // Form parameter name
.alwaysRemember(false)); "remember-me")
.rememberMeCookieName( // Cookie name
"remember-me")
.alwaysRemember(false));
}
http.authorizeHttpRequests( http.authorizeHttpRequests(
authz -> authz ->
authz.requestMatchers( authz.requestMatchers(
req -> { req -> {
String uri = req.getRequestURI(); String uri = req.getRequestURI();
String contextPath = req.getContextPath(); String contextPath = req.getContextPath();
// Remove the context path from the URI // Remove the context path from the URI
String trimmedUri = String trimmedUri =
uri.startsWith(contextPath) uri.startsWith(contextPath)
@ -224,22 +260,23 @@ public class SecurityConfiguration {
.anyRequest() .anyRequest()
.authenticated()); .authenticated());
// Handle User/Password Logins // Handle User/Password Logins
if (applicationProperties.getSecurity().isUserPass()) { if (securityProperties.isUserPass()) {
http.formLogin( http.formLogin(
formLogin -> formLogin ->
formLogin formLogin
.loginPage("/login") .loginPage("/login")
.successHandler( .successHandler(
new CustomAuthenticationSuccessHandler( new CustomAuthenticationSuccessHandler(
loginAttemptService, userService)) loginAttemptService,
userService,
jwtService))
.failureHandler( .failureHandler(
new CustomAuthenticationFailureHandler( new CustomAuthenticationFailureHandler(
loginAttemptService, userService)) loginAttemptService, userService))
.defaultSuccessUrl("/")
.permitAll()); .permitAll());
} }
// Handle OAUTH2 Logins // Handle OAUTH2 Logins
if (applicationProperties.getSecurity().isOauth2Active()) { if (securityProperties.isOauth2Active()) {
http.oauth2Login( http.oauth2Login(
oauth2 -> oauth2 ->
oauth2.loginPage("/oauth2") oauth2.loginPage("/oauth2")
@ -251,17 +288,17 @@ public class SecurityConfiguration {
.successHandler( .successHandler(
new CustomOAuth2AuthenticationSuccessHandler( new CustomOAuth2AuthenticationSuccessHandler(
loginAttemptService, loginAttemptService,
applicationProperties, securityProperties,
userService)) userService))
.failureHandler( .failureHandler(
new CustomOAuth2AuthenticationFailureHandler()) new CustomOAuth2AuthenticationFailureHandler())
. // Add existing Authorities from the database // Add existing Authorities from the database
userInfoEndpoint( .userInfoEndpoint(
userInfoEndpoint -> userInfoEndpoint ->
userInfoEndpoint userInfoEndpoint
.oidcUserService( .oidcUserService(
new CustomOAuth2UserService( new CustomOAuth2UserService(
applicationProperties, securityProperties,
userService, userService,
loginAttemptService)) loginAttemptService))
.userAuthoritiesMapper( .userAuthoritiesMapper(
@ -269,7 +306,7 @@ public class SecurityConfiguration {
.permitAll()); .permitAll());
} }
// Handle SAML // Handle SAML
if (applicationProperties.getSecurity().isSaml2Active() && runningProOrHigher) { if (securityProperties.isSaml2Active() && runningProOrHigher) {
// Configure the authentication provider // Configure the authentication provider
OpenSaml4AuthenticationProvider authenticationProvider = OpenSaml4AuthenticationProvider authenticationProvider =
new OpenSaml4AuthenticationProvider(); new OpenSaml4AuthenticationProvider();
@ -287,7 +324,7 @@ public class SecurityConfiguration {
.successHandler( .successHandler(
new CustomSaml2AuthenticationSuccessHandler( new CustomSaml2AuthenticationSuccessHandler(
loginAttemptService, loginAttemptService,
applicationProperties, securityProperties,
userService)) userService))
.failureHandler( .failureHandler(
new CustomSaml2AuthenticationFailureHandler()) new CustomSaml2AuthenticationFailureHandler())
@ -306,6 +343,13 @@ public class SecurityConfiguration {
return http.build(); return http.build();
} }
// todo: check if this is needed
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration)
throws Exception {
return configuration.getAuthenticationManager();
}
public DaoAuthenticationProvider daoAuthenticationProvider() { public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);
provider.setPasswordEncoder(passwordEncoder()); provider.setPasswordEncoder(passwordEncoder());

View File

@ -30,7 +30,7 @@ public class CustomOAuth2AuthenticationSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler { extends SavedRequestAwareAuthenticationSuccessHandler {
private final LoginAttemptService loginAttemptService; private final LoginAttemptService loginAttemptService;
private final ApplicationProperties applicationProperties; private final ApplicationProperties.Security securityProperties;
private final UserService userService; private final UserService userService;
@Override @Override
@ -60,7 +60,7 @@ public class CustomOAuth2AuthenticationSuccessHandler
// Redirect to the original destination // Redirect to the original destination
super.onAuthenticationSuccess(request, response, authentication); super.onAuthenticationSuccess(request, response, authentication);
} else { } else {
OAUTH2 oAuth = applicationProperties.getSecurity().getOauth2(); OAUTH2 oAuth = securityProperties.getOauth2();
if (loginAttemptService.isBlocked(username)) { if (loginAttemptService.isBlocked(username)) {
if (session != null) { if (session != null) {

View File

@ -30,7 +30,7 @@ public class CustomSaml2AuthenticationSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler { extends SavedRequestAwareAuthenticationSuccessHandler {
private LoginAttemptService loginAttemptService; private LoginAttemptService loginAttemptService;
private ApplicationProperties applicationProperties; private ApplicationProperties.Security securityProperties;
private UserService userService; private UserService userService;
@Override @Override
@ -65,7 +65,7 @@ public class CustomSaml2AuthenticationSuccessHandler
savedRequest.getRedirectUrl()); savedRequest.getRedirectUrl());
super.onAuthenticationSuccess(request, response, authentication); super.onAuthenticationSuccess(request, response, authentication);
} else { } else {
SAML2 saml2 = applicationProperties.getSecurity().getSaml2(); SAML2 saml2 = securityProperties.getSaml2();
log.debug( log.debug(
"Processing SAML2 authentication with autoCreateUser: {}", "Processing SAML2 authentication with autoCreateUser: {}",
saml2.getAutoCreateUser()); saml2.getAutoCreateUser());

View File

@ -27,13 +27,13 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
private final LoginAttemptService loginAttemptService; private final LoginAttemptService loginAttemptService;
private final ApplicationProperties applicationProperties; private final ApplicationProperties.Security securityProperties;
public CustomOAuth2UserService( public CustomOAuth2UserService(
ApplicationProperties applicationProperties, ApplicationProperties.Security securityProperties,
UserService userService, UserService userService,
LoginAttemptService loginAttemptService) { LoginAttemptService loginAttemptService) {
this.applicationProperties = applicationProperties; this.securityProperties = securityProperties;
this.userService = userService; this.userService = userService;
this.loginAttemptService = loginAttemptService; this.loginAttemptService = loginAttemptService;
} }
@ -42,7 +42,7 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
try { try {
OidcUser user = delegate.loadUser(userRequest); OidcUser user = delegate.loadUser(userRequest);
OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2(); OAUTH2 oauth2 = securityProperties.getOauth2();
UsernameAttribute usernameAttribute = UsernameAttribute usernameAttribute =
UsernameAttribute.valueOf(oauth2.getUseAsUsername().toUpperCase()); UsernameAttribute.valueOf(oauth2.getUseAsUsername().toUpperCase());
String usernameAttributeKey = usernameAttribute.getName(); String usernameAttributeKey = usernameAttribute.getName();

View File

@ -7,7 +7,6 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
@ -16,7 +15,7 @@ import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class CustomLogoutSuccessHandlerTest { class CustomLogoutSuccessHandlerTest {
@Mock private ApplicationProperties applicationProperties; @Mock private ApplicationProperties.Security securityProperties;
@InjectMocks private CustomLogoutSuccessHandler customLogoutSuccessHandler; @InjectMocks private CustomLogoutSuccessHandler customLogoutSuccessHandler;
@ -40,7 +39,6 @@ class CustomLogoutSuccessHandlerTest {
HttpServletRequest request = mock(HttpServletRequest.class); HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class); HttpServletResponse response = mock(HttpServletResponse.class);
OAuth2AuthenticationToken oAuth2AuthenticationToken = mock(OAuth2AuthenticationToken.class); OAuth2AuthenticationToken oAuth2AuthenticationToken = mock(OAuth2AuthenticationToken.class);
ApplicationProperties.Security security = mock(ApplicationProperties.Security.class);
ApplicationProperties.Security.OAUTH2 oauth = ApplicationProperties.Security.OAUTH2 oauth =
mock(ApplicationProperties.Security.OAUTH2.class); mock(ApplicationProperties.Security.OAUTH2.class);
@ -51,8 +49,7 @@ class CustomLogoutSuccessHandlerTest {
when(request.getServerName()).thenReturn("localhost"); when(request.getServerName()).thenReturn("localhost");
when(request.getServerPort()).thenReturn(8080); when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn(""); when(request.getContextPath()).thenReturn("");
when(applicationProperties.getSecurity()).thenReturn(security); when(securityProperties.getOauth2()).thenReturn(oauth);
when(security.getOauth2()).thenReturn(oauth);
when(oAuth2AuthenticationToken.getAuthorizedClientRegistrationId()).thenReturn("test"); when(oAuth2AuthenticationToken.getAuthorizedClientRegistrationId()).thenReturn("test");
customLogoutSuccessHandler.onLogoutSuccess(request, response, oAuth2AuthenticationToken); customLogoutSuccessHandler.onLogoutSuccess(request, response, oAuth2AuthenticationToken);
@ -67,7 +64,6 @@ class CustomLogoutSuccessHandlerTest {
HttpServletRequest request = mock(HttpServletRequest.class); HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class); HttpServletResponse response = mock(HttpServletResponse.class);
OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class);
ApplicationProperties.Security security = mock(ApplicationProperties.Security.class);
ApplicationProperties.Security.OAUTH2 oauth = ApplicationProperties.Security.OAUTH2 oauth =
mock(ApplicationProperties.Security.OAUTH2.class); mock(ApplicationProperties.Security.OAUTH2.class);
@ -81,8 +77,7 @@ class CustomLogoutSuccessHandlerTest {
when(request.getServerName()).thenReturn("localhost"); when(request.getServerName()).thenReturn("localhost");
when(request.getServerPort()).thenReturn(8080); when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn(""); when(request.getContextPath()).thenReturn("");
when(applicationProperties.getSecurity()).thenReturn(security); when(securityProperties.getOauth2()).thenReturn(oauth);
when(security.getOauth2()).thenReturn(oauth);
when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test"); when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test");
customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication); customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication);
@ -98,7 +93,6 @@ class CustomLogoutSuccessHandlerTest {
HttpServletRequest request = mock(HttpServletRequest.class); HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class); HttpServletResponse response = mock(HttpServletResponse.class);
OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class);
ApplicationProperties.Security security = mock(ApplicationProperties.Security.class);
ApplicationProperties.Security.OAUTH2 oauth = ApplicationProperties.Security.OAUTH2 oauth =
mock(ApplicationProperties.Security.OAUTH2.class); mock(ApplicationProperties.Security.OAUTH2.class);
@ -108,8 +102,7 @@ class CustomLogoutSuccessHandlerTest {
when(request.getServerName()).thenReturn("localhost"); when(request.getServerName()).thenReturn("localhost");
when(request.getServerPort()).thenReturn(8080); when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn(""); when(request.getContextPath()).thenReturn("");
when(applicationProperties.getSecurity()).thenReturn(security); when(securityProperties.getOauth2()).thenReturn(oauth);
when(security.getOauth2()).thenReturn(oauth);
when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test"); when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test");
customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication); customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication);
@ -124,7 +117,6 @@ class CustomLogoutSuccessHandlerTest {
HttpServletRequest request = mock(HttpServletRequest.class); HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class); HttpServletResponse response = mock(HttpServletResponse.class);
OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class);
ApplicationProperties.Security security = mock(ApplicationProperties.Security.class);
ApplicationProperties.Security.OAUTH2 oauth = ApplicationProperties.Security.OAUTH2 oauth =
mock(ApplicationProperties.Security.OAUTH2.class); mock(ApplicationProperties.Security.OAUTH2.class);
@ -135,8 +127,7 @@ class CustomLogoutSuccessHandlerTest {
when(request.getServerName()).thenReturn("localhost"); when(request.getServerName()).thenReturn("localhost");
when(request.getServerPort()).thenReturn(8080); when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn(""); when(request.getContextPath()).thenReturn("");
when(applicationProperties.getSecurity()).thenReturn(security); when(securityProperties.getOauth2()).thenReturn(oauth);
when(security.getOauth2()).thenReturn(oauth);
when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test"); when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test");
customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication); customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication);
@ -151,7 +142,6 @@ class CustomLogoutSuccessHandlerTest {
HttpServletRequest request = mock(HttpServletRequest.class); HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class); HttpServletResponse response = mock(HttpServletResponse.class);
OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class);
ApplicationProperties.Security security = mock(ApplicationProperties.Security.class);
ApplicationProperties.Security.OAUTH2 oauth = ApplicationProperties.Security.OAUTH2 oauth =
mock(ApplicationProperties.Security.OAUTH2.class); mock(ApplicationProperties.Security.OAUTH2.class);
@ -164,8 +154,7 @@ class CustomLogoutSuccessHandlerTest {
when(request.getServerName()).thenReturn("localhost"); when(request.getServerName()).thenReturn("localhost");
when(request.getServerPort()).thenReturn(8080); when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn(""); when(request.getContextPath()).thenReturn("");
when(applicationProperties.getSecurity()).thenReturn(security); when(securityProperties.getOauth2()).thenReturn(oauth);
when(security.getOauth2()).thenReturn(oauth);
when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test"); when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test");
customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication); customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication);
@ -180,7 +169,6 @@ class CustomLogoutSuccessHandlerTest {
HttpServletRequest request = mock(HttpServletRequest.class); HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class); HttpServletResponse response = mock(HttpServletResponse.class);
OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class);
ApplicationProperties.Security security = mock(ApplicationProperties.Security.class);
ApplicationProperties.Security.OAUTH2 oauth = ApplicationProperties.Security.OAUTH2 oauth =
mock(ApplicationProperties.Security.OAUTH2.class); mock(ApplicationProperties.Security.OAUTH2.class);
@ -195,8 +183,7 @@ class CustomLogoutSuccessHandlerTest {
when(request.getServerName()).thenReturn("localhost"); when(request.getServerName()).thenReturn("localhost");
when(request.getServerPort()).thenReturn(8080); when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn(""); when(request.getContextPath()).thenReturn("");
when(applicationProperties.getSecurity()).thenReturn(security); when(securityProperties.getOauth2()).thenReturn(oauth);
when(security.getOauth2()).thenReturn(oauth);
when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test"); when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test");
customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication); customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication);
@ -211,7 +198,6 @@ class CustomLogoutSuccessHandlerTest {
HttpServletRequest request = mock(HttpServletRequest.class); HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class); HttpServletResponse response = mock(HttpServletResponse.class);
OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class);
ApplicationProperties.Security security = mock(ApplicationProperties.Security.class);
ApplicationProperties.Security.OAUTH2 oauth = ApplicationProperties.Security.OAUTH2 oauth =
mock(ApplicationProperties.Security.OAUTH2.class); mock(ApplicationProperties.Security.OAUTH2.class);
@ -227,8 +213,7 @@ class CustomLogoutSuccessHandlerTest {
when(request.getServerName()).thenReturn("localhost"); when(request.getServerName()).thenReturn("localhost");
when(request.getServerPort()).thenReturn(8080); when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn(""); when(request.getContextPath()).thenReturn("");
when(applicationProperties.getSecurity()).thenReturn(security); when(securityProperties.getOauth2()).thenReturn(oauth);
when(security.getOauth2()).thenReturn(oauth);
when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test"); when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test");
customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication); customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication);
@ -243,7 +228,6 @@ class CustomLogoutSuccessHandlerTest {
HttpServletRequest request = mock(HttpServletRequest.class); HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class); HttpServletResponse response = mock(HttpServletResponse.class);
OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class);
ApplicationProperties.Security security = mock(ApplicationProperties.Security.class);
ApplicationProperties.Security.OAUTH2 oauth = ApplicationProperties.Security.OAUTH2 oauth =
mock(ApplicationProperties.Security.OAUTH2.class); mock(ApplicationProperties.Security.OAUTH2.class);
@ -256,8 +240,7 @@ class CustomLogoutSuccessHandlerTest {
when(request.getServerName()).thenReturn("localhost"); when(request.getServerName()).thenReturn("localhost");
when(request.getServerPort()).thenReturn(8080); when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn(""); when(request.getContextPath()).thenReturn("");
when(applicationProperties.getSecurity()).thenReturn(security); when(securityProperties.getOauth2()).thenReturn(oauth);
when(security.getOauth2()).thenReturn(oauth);
when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test"); when(authentication.getAuthorizedClientRegistrationId()).thenReturn("test");
customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication); customLogoutSuccessHandler.onLogoutSuccess(request, response, authentication);