From 4e7c320e8e28cd777b80c68b9f5320e96fb9f156 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 3 Apr 2024 19:17:37 +0300 Subject: [PATCH 1/7] Upgrade to Spring Boot 3/Spring Security 6 Signed-off-by: Lauris Kaplinski --- .github/workflows/maven-build.yml | 12 ++-- README.md | 2 +- pom.xml | 6 +- .../WebEidSpringbootExampleApplication.java | 2 +- .../config/ApplicationConfiguration.java | 55 +++++++++---------- .../config/SameSiteCookieConfiguration.java | 2 +- .../SessionBackedChallengeNonceStore.java | 4 +- .../config/ValidationConfiguration.java | 4 +- .../eu/webeid/example/config/YAMLConfig.java | 2 +- .../AuthTokenDTOAuthenticationProvider.java | 2 +- .../WebEidAjaxLoginProcessingFilter.java | 21 +++++-- .../security/WebEidAuthentication.java | 2 +- .../AjaxAuthenticationFailureHandler.java | 8 +-- .../AjaxAuthenticationSuccessHandler.java | 6 +- .../example/security/dto/AuthTokenDTO.java | 2 +- .../example/service/SigningService.java | 6 +- .../example/service/dto/CertificateDTO.java | 2 +- .../example/service/dto/ChallengeDTO.java | 2 +- .../webeid/example/service/dto/DigestDTO.java | 2 +- .../webeid/example/service/dto/FileDTO.java | 2 +- .../service/dto/SignatureAlgorithmDTO.java | 2 +- .../example/service/dto/SignatureDTO.java | 2 +- .../webeid/example/web/IndexController.java | 37 +++++++++++++ .../webeid/example/web/WelcomeController.java | 4 +- .../example/web/rest/ChallengeController.java | 2 +- .../example/web/rest/SigningController.java | 2 +- src/main/resources/static/js/errors.js | 2 +- src/main/resources/templates/index.html | 2 +- .../AuthenticationRestControllerTest.java | 2 +- .../eu/webeid/example/WebApplicationTest.java | 4 +- .../WebEidAjaxLoginProcessingFilterTest.java | 8 ++- .../eu/webeid/example/testutil/Dates.java | 2 +- .../webeid/example/testutil/HttpHelper.java | 2 +- .../webeid/example/testutil/ObjectMother.java | 4 +- 34 files changed, 135 insertions(+), 84 deletions(-) create mode 100644 src/main/java/eu/webeid/example/web/IndexController.java diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 7b3120c..14becab 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -7,19 +7,19 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: zulu - java-version: 11 + java-version: 17 - name: Cache Maven packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.m2 - key: ${{ runner.os }}-m2-v11-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2-v11-${{ secrets.CACHE_VERSION }} + key: ${{ runner.os }}-m2-v17-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2-v17-${{ secrets.CACHE_VERSION }} - name: Build run: mvn --batch-mode compile diff --git a/README.md b/README.md index 55049e7..008357d 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ You can specify the profile as a command-line argument to the Maven wrapper comm ### 5. Run the application -Spring Boot web applications can be run from the command-line. You need to have the Java Development Kit 8 installed for building the application package and running the application. +Spring Boot web applications can be run from the command-line. You need to have the Java Development Kit 17 installed for building the application package and running the application. Build and run the application with the following command in a terminal window: diff --git a/pom.xml b/pom.xml index 3568a9c..49761fe 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.15 + 3.1.9 org.webeid.example @@ -17,10 +17,10 @@ - 11 + 17 2.22.1 3.0.0 - 5.2.0 + 5.3.0 1.44 diff --git a/src/main/java/eu/webeid/example/WebEidSpringbootExampleApplication.java b/src/main/java/eu/webeid/example/WebEidSpringbootExampleApplication.java index 2af43ab..f82bac0 100644 --- a/src/main/java/eu/webeid/example/WebEidSpringbootExampleApplication.java +++ b/src/main/java/eu/webeid/example/WebEidSpringbootExampleApplication.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java b/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java index 96430c1..5e974e4 100644 --- a/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java +++ b/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,53 +24,52 @@ import eu.webeid.example.security.AuthTokenDTOAuthenticationProvider; import eu.webeid.example.security.WebEidAjaxLoginProcessingFilter; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +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.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableWebSecurity -@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) -public class ApplicationConfiguration extends WebSecurityConfigurerAdapter implements WebMvcConfigurer { +@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true) +public class ApplicationConfiguration implements WebMvcConfigurer { final AuthTokenDTOAuthenticationProvider authTokenDTOAuthenticationProvider; + final SecurityContextRepository securityContextRepository; public ApplicationConfiguration(AuthTokenDTOAuthenticationProvider authTokenDTOAuthenticationProvider) { this.authTokenDTOAuthenticationProvider = authTokenDTOAuthenticationProvider; + this.securityContextRepository = new HttpSessionSecurityContextRepository(); } - @Override - protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) { - authenticationManagerBuilder.authenticationProvider(authTokenDTOAuthenticationProvider); + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { + return authenticationConfiguration.getAuthenticationManager(); } - @Override - protected void configure(HttpSecurity http) throws Exception { - // @formatter:off - http - .addFilterBefore( - new WebEidAjaxLoginProcessingFilter("/auth/login", authenticationManager()), - UsernamePasswordAuthenticationFilter.class) - .authorizeRequests() - .antMatchers("/auth/challenge", "/auth/login", "/") - .permitAll() - .antMatchers("/welcome") - .authenticated() - .and() - .logout() - .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()) - .and() - .headers() - .frameOptions().sameOrigin(); - // @formatter:on + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + AuthenticationManager manager = authenticationManager(http.getSharedObject(AuthenticationConfiguration.class)); + + return http + .authenticationProvider(authTokenDTOAuthenticationProvider) + .addFilterBefore(new WebEidAjaxLoginProcessingFilter("/auth/login", manager, securityContextRepository), + UsernamePasswordAuthenticationFilter.class) + .logout(logout -> logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())) + .headers(headers -> headers.frameOptions(options -> options.sameOrigin())) + .build(); } + @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/welcome").setViewName("welcome"); diff --git a/src/main/java/eu/webeid/example/config/SameSiteCookieConfiguration.java b/src/main/java/eu/webeid/example/config/SameSiteCookieConfiguration.java index 1b87329..7940165 100644 --- a/src/main/java/eu/webeid/example/config/SameSiteCookieConfiguration.java +++ b/src/main/java/eu/webeid/example/config/SameSiteCookieConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/config/SessionBackedChallengeNonceStore.java b/src/main/java/eu/webeid/example/config/SessionBackedChallengeNonceStore.java index 00e0b9f..cb4654d 100644 --- a/src/main/java/eu/webeid/example/config/SessionBackedChallengeNonceStore.java +++ b/src/main/java/eu/webeid/example/config/SessionBackedChallengeNonceStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,7 @@ import eu.webeid.security.challenge.ChallengeNonce; import eu.webeid.security.challenge.ChallengeNonceStore; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpSession; public class SessionBackedChallengeNonceStore implements ChallengeNonceStore { diff --git a/src/main/java/eu/webeid/example/config/ValidationConfiguration.java b/src/main/java/eu/webeid/example/config/ValidationConfiguration.java index f1f78f7..dbe21ee 100644 --- a/src/main/java/eu/webeid/example/config/ValidationConfiguration.java +++ b/src/main/java/eu/webeid/example/config/ValidationConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,7 +37,7 @@ import eu.webeid.security.validator.AuthTokenValidator; import eu.webeid.security.validator.AuthTokenValidatorBuilder; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpSession; import java.io.IOException; import java.io.InputStream; import java.net.URI; diff --git a/src/main/java/eu/webeid/example/config/YAMLConfig.java b/src/main/java/eu/webeid/example/config/YAMLConfig.java index e8fecd3..35905f0 100644 --- a/src/main/java/eu/webeid/example/config/YAMLConfig.java +++ b/src/main/java/eu/webeid/example/config/YAMLConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/security/AuthTokenDTOAuthenticationProvider.java b/src/main/java/eu/webeid/example/security/AuthTokenDTOAuthenticationProvider.java index 95ea1ee..03e535f 100644 --- a/src/main/java/eu/webeid/example/security/AuthTokenDTOAuthenticationProvider.java +++ b/src/main/java/eu/webeid/example/security/AuthTokenDTOAuthenticationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java b/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java index 16bf0c4..eb690d8 100644 --- a/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java +++ b/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,12 +24,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import eu.webeid.example.security.ajax.AjaxAuthenticationFailureHandler; import eu.webeid.example.security.ajax.AjaxAuthenticationSuccessHandler; import eu.webeid.example.security.dto.AuthTokenDTO; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; @@ -37,22 +39,27 @@ import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy; +import org.springframework.security.web.context.SecurityContextRepository; public class WebEidAjaxLoginProcessingFilter extends AbstractAuthenticationProcessingFilter { private static final Logger LOG = LoggerFactory.getLogger(WebEidAjaxLoginProcessingFilter.class); + private final SecurityContextRepository securityContextRepository; public WebEidAjaxLoginProcessingFilter( String defaultFilterProcessesUrl, - AuthenticationManager authenticationManager + AuthenticationManager authenticationManager, + SecurityContextRepository securityContextRepository ) { super(defaultFilterProcessesUrl); this.setAuthenticationManager(authenticationManager); this.setAuthenticationSuccessHandler(new AjaxAuthenticationSuccessHandler()); this.setAuthenticationFailureHandler(new AjaxAuthenticationFailureHandler()); setSessionAuthenticationStrategy(new SessionFixationProtectionStrategy()); + this.securityContextRepository = securityContextRepository; } @Override @@ -76,4 +83,10 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ LOG.info("attemptAuthentication(): Calling authentication manager"); return getAuthenticationManager().authenticate(token); } + + @Override + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { + super.successfulAuthentication(request, response, chain, authResult); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/OverriddenMethodBody + securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response); + } } diff --git a/src/main/java/eu/webeid/example/security/WebEidAuthentication.java b/src/main/java/eu/webeid/example/security/WebEidAuthentication.java index 1726ff1..4a67020 100644 --- a/src/main/java/eu/webeid/example/security/WebEidAuthentication.java +++ b/src/main/java/eu/webeid/example/security/WebEidAuthentication.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationFailureHandler.java b/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationFailureHandler.java index d7c308e..647698f 100644 --- a/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationFailureHandler.java +++ b/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationFailureHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,9 +27,9 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import java.io.IOException; public class AjaxAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { diff --git a/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java b/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java index e1e0db0..b7b70b9 100644 --- a/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java +++ b/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,8 +29,8 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; diff --git a/src/main/java/eu/webeid/example/security/dto/AuthTokenDTO.java b/src/main/java/eu/webeid/example/security/dto/AuthTokenDTO.java index c0f4cd2..9321c4c 100644 --- a/src/main/java/eu/webeid/example/security/dto/AuthTokenDTO.java +++ b/src/main/java/eu/webeid/example/security/dto/AuthTokenDTO.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/service/SigningService.java b/src/main/java/eu/webeid/example/service/SigningService.java index 1307bd0..69adc1c 100644 --- a/src/main/java/eu/webeid/example/service/SigningService.java +++ b/src/main/java/eu/webeid/example/service/SigningService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -46,8 +46,8 @@ import org.springframework.core.io.ByteArrayResource; import org.springframework.stereotype.Service; -import javax.servlet.http.HttpSession; -import javax.xml.bind.DatatypeConverter; +import jakarta.servlet.http.HttpSession; +import jakarta.xml.bind.DatatypeConverter; import java.io.IOException; import java.io.InputStream; import java.security.NoSuchAlgorithmException; diff --git a/src/main/java/eu/webeid/example/service/dto/CertificateDTO.java b/src/main/java/eu/webeid/example/service/dto/CertificateDTO.java index 4148165..6050c85 100644 --- a/src/main/java/eu/webeid/example/service/dto/CertificateDTO.java +++ b/src/main/java/eu/webeid/example/service/dto/CertificateDTO.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/service/dto/ChallengeDTO.java b/src/main/java/eu/webeid/example/service/dto/ChallengeDTO.java index a882db2..dd95d42 100644 --- a/src/main/java/eu/webeid/example/service/dto/ChallengeDTO.java +++ b/src/main/java/eu/webeid/example/service/dto/ChallengeDTO.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/service/dto/DigestDTO.java b/src/main/java/eu/webeid/example/service/dto/DigestDTO.java index 483a71b..4e56d36 100644 --- a/src/main/java/eu/webeid/example/service/dto/DigestDTO.java +++ b/src/main/java/eu/webeid/example/service/dto/DigestDTO.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/service/dto/FileDTO.java b/src/main/java/eu/webeid/example/service/dto/FileDTO.java index af2e24e..dca653b 100644 --- a/src/main/java/eu/webeid/example/service/dto/FileDTO.java +++ b/src/main/java/eu/webeid/example/service/dto/FileDTO.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/service/dto/SignatureAlgorithmDTO.java b/src/main/java/eu/webeid/example/service/dto/SignatureAlgorithmDTO.java index 94d1b8c..bef5ba4 100644 --- a/src/main/java/eu/webeid/example/service/dto/SignatureAlgorithmDTO.java +++ b/src/main/java/eu/webeid/example/service/dto/SignatureAlgorithmDTO.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/service/dto/SignatureDTO.java b/src/main/java/eu/webeid/example/service/dto/SignatureDTO.java index 5a416cc..68742fc 100644 --- a/src/main/java/eu/webeid/example/service/dto/SignatureDTO.java +++ b/src/main/java/eu/webeid/example/service/dto/SignatureDTO.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/web/IndexController.java b/src/main/java/eu/webeid/example/web/IndexController.java new file mode 100644 index 0000000..e464a50 --- /dev/null +++ b/src/main/java/eu/webeid/example/web/IndexController.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020-2024 Estonian Information System Authority + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package eu.webeid.example.web; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class IndexController { + @GetMapping("/") + public String welcome(Model model, HttpServletRequest request) { + model.addAttribute("serverName", request.getServerName()); + return "index"; + } +} diff --git a/src/main/java/eu/webeid/example/web/WelcomeController.java b/src/main/java/eu/webeid/example/web/WelcomeController.java index dcd09da..e61fcc2 100644 --- a/src/main/java/eu/webeid/example/web/WelcomeController.java +++ b/src/main/java/eu/webeid/example/web/WelcomeController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotNull; import java.security.Principal; import static eu.webeid.example.security.AuthTokenDTOAuthenticationProvider.ROLE_USER; diff --git a/src/main/java/eu/webeid/example/web/rest/ChallengeController.java b/src/main/java/eu/webeid/example/web/rest/ChallengeController.java index a81aa68..9640fe6 100644 --- a/src/main/java/eu/webeid/example/web/rest/ChallengeController.java +++ b/src/main/java/eu/webeid/example/web/rest/ChallengeController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/eu/webeid/example/web/rest/SigningController.java b/src/main/java/eu/webeid/example/web/rest/SigningController.java index abdda22..14ecfae 100644 --- a/src/main/java/eu/webeid/example/web/rest/SigningController.java +++ b/src/main/java/eu/webeid/example/web/rest/SigningController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/resources/static/js/errors.js b/src/main/resources/static/js/errors.js index 1665e6d..95220bb 100644 --- a/src/main/resources/static/js/errors.js +++ b/src/main/resources/static/js/errors.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 429b6b3..759d337 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -57,7 +57,7 @@

Usage

  • on Ubuntu Linux, for Firefox and Chrome, download and execute the
    download-install-web-eid.sh script from the console with
    - wget -O - https:///scripts/download-install-web-eid.sh + wget -O - https:///scripts/download-install-web-eid.sh | bash
    Note that Firefox is installed with Snap in Ubuntu 22.04 or later by default and as the Snap sandbox does not allow communication with the external native messaging host, Web diff --git a/src/test/java/eu/webeid/example/AuthenticationRestControllerTest.java b/src/test/java/eu/webeid/example/AuthenticationRestControllerTest.java index fcd4214..aa6f5df 100644 --- a/src/test/java/eu/webeid/example/AuthenticationRestControllerTest.java +++ b/src/test/java/eu/webeid/example/AuthenticationRestControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/eu/webeid/example/WebApplicationTest.java b/src/test/java/eu/webeid/example/WebApplicationTest.java index 643734e..d6e343b 100644 --- a/src/test/java/eu/webeid/example/WebApplicationTest.java +++ b/src/test/java/eu/webeid/example/WebApplicationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -59,7 +59,7 @@ public class WebApplicationTest { private WebApplicationContext context; @Autowired - private javax.servlet.Filter[] springSecurityFilterChain; + private jakarta.servlet.Filter[] springSecurityFilterChain; private static DefaultMockMvcBuilder mvcBuilder; diff --git a/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java b/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java index 0640a4d..adbaff5 100644 --- a/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java +++ b/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java @@ -4,14 +4,15 @@ import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.StringReader; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import org.springframework.security.web.context.SecurityContextRepository; class WebEidAjaxLoginProcessingFilterTest { @@ -31,9 +32,10 @@ void testAttemptAuthentication() throws Exception { when(request.getReader()).thenReturn(new BufferedReader(new StringReader(AUTH_TOKEN))); final AuthenticationManager authenticationManager = mock(AuthenticationManager.class); + final SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class); assertDoesNotThrow(() -> - new WebEidAjaxLoginProcessingFilter("/auth/login", authenticationManager) + new WebEidAjaxLoginProcessingFilter("/auth/login", authenticationManager, securityContextRepository) .attemptAuthentication(request, response)); } } \ No newline at end of file diff --git a/src/test/java/eu/webeid/example/testutil/Dates.java b/src/test/java/eu/webeid/example/testutil/Dates.java index 9ab1260..c44118d 100644 --- a/src/test/java/eu/webeid/example/testutil/Dates.java +++ b/src/test/java/eu/webeid/example/testutil/Dates.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/eu/webeid/example/testutil/HttpHelper.java b/src/test/java/eu/webeid/example/testutil/HttpHelper.java index 03ae120..fec2621 100644 --- a/src/test/java/eu/webeid/example/testutil/HttpHelper.java +++ b/src/test/java/eu/webeid/example/testutil/HttpHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/eu/webeid/example/testutil/ObjectMother.java b/src/test/java/eu/webeid/example/testutil/ObjectMother.java index e6de802..f6103d5 100644 --- a/src/test/java/eu/webeid/example/testutil/ObjectMother.java +++ b/src/test/java/eu/webeid/example/testutil/ObjectMother.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023 Estonian Information System Authority + * Copyright (c) 2020-2024 Estonian Information System Authority * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,7 +33,7 @@ import eu.webeid.example.service.dto.CertificateDTO; import eu.webeid.example.service.dto.SignatureDTO; -import javax.xml.bind.DatatypeConverter; +import jakarta.xml.bind.DatatypeConverter; import java.io.FileInputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; From 055fc4c82390e5dcecd8026e2c8bfae6da961bdc Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 3 Apr 2024 19:17:37 +0300 Subject: [PATCH 2/7] Upgrade to Spring Boot 3/Spring Security 6 Signed-off-by: Lauris Kaplinski --- .github/workflows/maven-build.yml | 12 ++--- README.md | 2 +- pom.xml | 6 +-- .../config/ApplicationConfiguration.java | 53 +++++++++---------- .../SessionBackedChallengeNonceStore.java | 2 +- .../config/ValidationConfiguration.java | 2 +- .../WebEidAjaxLoginProcessingFilter.java | 19 +++++-- .../AjaxAuthenticationFailureHandler.java | 6 +-- .../AjaxAuthenticationSuccessHandler.java | 4 +- .../example/service/SigningService.java | 4 +- .../webeid/example/web/IndexController.java | 37 +++++++++++++ .../webeid/example/web/WelcomeController.java | 2 +- src/main/resources/templates/index.html | 2 +- .../eu/webeid/example/WebApplicationTest.java | 2 +- .../WebEidAjaxLoginProcessingFilterTest.java | 8 +-- .../webeid/example/testutil/ObjectMother.java | 2 +- 16 files changed, 107 insertions(+), 56 deletions(-) create mode 100644 src/main/java/eu/webeid/example/web/IndexController.java diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 7b3120c..14becab 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -7,19 +7,19 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: zulu - java-version: 11 + java-version: 17 - name: Cache Maven packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.m2 - key: ${{ runner.os }}-m2-v11-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2-v11-${{ secrets.CACHE_VERSION }} + key: ${{ runner.os }}-m2-v17-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2-v17-${{ secrets.CACHE_VERSION }} - name: Build run: mvn --batch-mode compile diff --git a/README.md b/README.md index 55049e7..008357d 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ You can specify the profile as a command-line argument to the Maven wrapper comm ### 5. Run the application -Spring Boot web applications can be run from the command-line. You need to have the Java Development Kit 8 installed for building the application package and running the application. +Spring Boot web applications can be run from the command-line. You need to have the Java Development Kit 17 installed for building the application package and running the application. Build and run the application with the following command in a terminal window: diff --git a/pom.xml b/pom.xml index 3568a9c..49761fe 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.15 + 3.1.9 org.webeid.example @@ -17,10 +17,10 @@ - 11 + 17 2.22.1 3.0.0 - 5.2.0 + 5.3.0 1.44 diff --git a/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java b/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java index 9fba315..5e974e4 100644 --- a/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java +++ b/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java @@ -24,53 +24,52 @@ import eu.webeid.example.security.AuthTokenDTOAuthenticationProvider; import eu.webeid.example.security.WebEidAjaxLoginProcessingFilter; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +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.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableWebSecurity -@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) -public class ApplicationConfiguration extends WebSecurityConfigurerAdapter implements WebMvcConfigurer { +@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true) +public class ApplicationConfiguration implements WebMvcConfigurer { final AuthTokenDTOAuthenticationProvider authTokenDTOAuthenticationProvider; + final SecurityContextRepository securityContextRepository; public ApplicationConfiguration(AuthTokenDTOAuthenticationProvider authTokenDTOAuthenticationProvider) { this.authTokenDTOAuthenticationProvider = authTokenDTOAuthenticationProvider; + this.securityContextRepository = new HttpSessionSecurityContextRepository(); } - @Override - protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) { - authenticationManagerBuilder.authenticationProvider(authTokenDTOAuthenticationProvider); + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { + return authenticationConfiguration.getAuthenticationManager(); } - @Override - protected void configure(HttpSecurity http) throws Exception { - // @formatter:off - http - .addFilterBefore( - new WebEidAjaxLoginProcessingFilter("/auth/login", authenticationManager()), - UsernamePasswordAuthenticationFilter.class) - .authorizeRequests() - .antMatchers("/auth/challenge", "/auth/login", "/") - .permitAll() - .antMatchers("/welcome") - .authenticated() - .and() - .logout() - .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()) - .and() - .headers() - .frameOptions().sameOrigin(); - // @formatter:on + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + AuthenticationManager manager = authenticationManager(http.getSharedObject(AuthenticationConfiguration.class)); + + return http + .authenticationProvider(authTokenDTOAuthenticationProvider) + .addFilterBefore(new WebEidAjaxLoginProcessingFilter("/auth/login", manager, securityContextRepository), + UsernamePasswordAuthenticationFilter.class) + .logout(logout -> logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())) + .headers(headers -> headers.frameOptions(options -> options.sameOrigin())) + .build(); } + @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/welcome").setViewName("welcome"); diff --git a/src/main/java/eu/webeid/example/config/SessionBackedChallengeNonceStore.java b/src/main/java/eu/webeid/example/config/SessionBackedChallengeNonceStore.java index c94a324..cb4654d 100644 --- a/src/main/java/eu/webeid/example/config/SessionBackedChallengeNonceStore.java +++ b/src/main/java/eu/webeid/example/config/SessionBackedChallengeNonceStore.java @@ -26,7 +26,7 @@ import eu.webeid.security.challenge.ChallengeNonce; import eu.webeid.security.challenge.ChallengeNonceStore; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpSession; public class SessionBackedChallengeNonceStore implements ChallengeNonceStore { diff --git a/src/main/java/eu/webeid/example/config/ValidationConfiguration.java b/src/main/java/eu/webeid/example/config/ValidationConfiguration.java index 83f0f47..dbe21ee 100644 --- a/src/main/java/eu/webeid/example/config/ValidationConfiguration.java +++ b/src/main/java/eu/webeid/example/config/ValidationConfiguration.java @@ -37,7 +37,7 @@ import eu.webeid.security.validator.AuthTokenValidator; import eu.webeid.security.validator.AuthTokenValidatorBuilder; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpSession; import java.io.IOException; import java.io.InputStream; import java.net.URI; diff --git a/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java b/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java index ac43205..eb690d8 100644 --- a/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java +++ b/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java @@ -24,12 +24,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import eu.webeid.example.security.ajax.AjaxAuthenticationFailureHandler; import eu.webeid.example.security.ajax.AjaxAuthenticationSuccessHandler; import eu.webeid.example.security.dto.AuthTokenDTO; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; @@ -37,22 +39,27 @@ import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy; +import org.springframework.security.web.context.SecurityContextRepository; public class WebEidAjaxLoginProcessingFilter extends AbstractAuthenticationProcessingFilter { private static final Logger LOG = LoggerFactory.getLogger(WebEidAjaxLoginProcessingFilter.class); + private final SecurityContextRepository securityContextRepository; public WebEidAjaxLoginProcessingFilter( String defaultFilterProcessesUrl, - AuthenticationManager authenticationManager + AuthenticationManager authenticationManager, + SecurityContextRepository securityContextRepository ) { super(defaultFilterProcessesUrl); this.setAuthenticationManager(authenticationManager); this.setAuthenticationSuccessHandler(new AjaxAuthenticationSuccessHandler()); this.setAuthenticationFailureHandler(new AjaxAuthenticationFailureHandler()); setSessionAuthenticationStrategy(new SessionFixationProtectionStrategy()); + this.securityContextRepository = securityContextRepository; } @Override @@ -76,4 +83,10 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ LOG.info("attemptAuthentication(): Calling authentication manager"); return getAuthenticationManager().authenticate(token); } + + @Override + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { + super.successfulAuthentication(request, response, chain, authResult); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/OverriddenMethodBody + securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response); + } } diff --git a/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationFailureHandler.java b/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationFailureHandler.java index 8580bca..647698f 100644 --- a/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationFailureHandler.java +++ b/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationFailureHandler.java @@ -27,9 +27,9 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import java.io.IOException; public class AjaxAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { diff --git a/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java b/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java index 19d0410..b7b70b9 100644 --- a/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java +++ b/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java @@ -29,8 +29,8 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; diff --git a/src/main/java/eu/webeid/example/service/SigningService.java b/src/main/java/eu/webeid/example/service/SigningService.java index a7d71b4..69adc1c 100644 --- a/src/main/java/eu/webeid/example/service/SigningService.java +++ b/src/main/java/eu/webeid/example/service/SigningService.java @@ -46,8 +46,8 @@ import org.springframework.core.io.ByteArrayResource; import org.springframework.stereotype.Service; -import javax.servlet.http.HttpSession; -import javax.xml.bind.DatatypeConverter; +import jakarta.servlet.http.HttpSession; +import jakarta.xml.bind.DatatypeConverter; import java.io.IOException; import java.io.InputStream; import java.security.NoSuchAlgorithmException; diff --git a/src/main/java/eu/webeid/example/web/IndexController.java b/src/main/java/eu/webeid/example/web/IndexController.java new file mode 100644 index 0000000..e464a50 --- /dev/null +++ b/src/main/java/eu/webeid/example/web/IndexController.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020-2024 Estonian Information System Authority + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package eu.webeid.example.web; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class IndexController { + @GetMapping("/") + public String welcome(Model model, HttpServletRequest request) { + model.addAttribute("serverName", request.getServerName()); + return "index"; + } +} diff --git a/src/main/java/eu/webeid/example/web/WelcomeController.java b/src/main/java/eu/webeid/example/web/WelcomeController.java index deb7ab8..e61fcc2 100644 --- a/src/main/java/eu/webeid/example/web/WelcomeController.java +++ b/src/main/java/eu/webeid/example/web/WelcomeController.java @@ -29,7 +29,7 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotNull; import java.security.Principal; import static eu.webeid.example.security.AuthTokenDTOAuthenticationProvider.ROLE_USER; diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 6a86740..5836d2e 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -57,7 +57,7 @@

    Usage

  • on Ubuntu Linux, for Firefox and Chrome, download and execute the
    download-install-web-eid.sh script from the console with
    - wget -O - https:///scripts/download-install-web-eid.sh + wget -O - https:///scripts/download-install-web-eid.sh | bash
    Note: as of the 2.5 version, Web eID supports Firefox installed via Snap.
  • diff --git a/src/test/java/eu/webeid/example/WebApplicationTest.java b/src/test/java/eu/webeid/example/WebApplicationTest.java index 4d95f43..d6e343b 100644 --- a/src/test/java/eu/webeid/example/WebApplicationTest.java +++ b/src/test/java/eu/webeid/example/WebApplicationTest.java @@ -59,7 +59,7 @@ public class WebApplicationTest { private WebApplicationContext context; @Autowired - private javax.servlet.Filter[] springSecurityFilterChain; + private jakarta.servlet.Filter[] springSecurityFilterChain; private static DefaultMockMvcBuilder mvcBuilder; diff --git a/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java b/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java index 0640a4d..adbaff5 100644 --- a/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java +++ b/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java @@ -4,14 +4,15 @@ import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.StringReader; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import org.springframework.security.web.context.SecurityContextRepository; class WebEidAjaxLoginProcessingFilterTest { @@ -31,9 +32,10 @@ void testAttemptAuthentication() throws Exception { when(request.getReader()).thenReturn(new BufferedReader(new StringReader(AUTH_TOKEN))); final AuthenticationManager authenticationManager = mock(AuthenticationManager.class); + final SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class); assertDoesNotThrow(() -> - new WebEidAjaxLoginProcessingFilter("/auth/login", authenticationManager) + new WebEidAjaxLoginProcessingFilter("/auth/login", authenticationManager, securityContextRepository) .attemptAuthentication(request, response)); } } \ No newline at end of file diff --git a/src/test/java/eu/webeid/example/testutil/ObjectMother.java b/src/test/java/eu/webeid/example/testutil/ObjectMother.java index ad048fd..f6103d5 100644 --- a/src/test/java/eu/webeid/example/testutil/ObjectMother.java +++ b/src/test/java/eu/webeid/example/testutil/ObjectMother.java @@ -33,7 +33,7 @@ import eu.webeid.example.service.dto.CertificateDTO; import eu.webeid.example.service.dto.SignatureDTO; -import javax.xml.bind.DatatypeConverter; +import jakarta.xml.bind.DatatypeConverter; import java.io.FileInputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; From cc660a550a0b06155238dc5cec1794c495f8f953 Mon Sep 17 00:00:00 2001 From: Mart Somermaa Date: Fri, 5 Apr 2024 17:37:10 +0300 Subject: [PATCH 3/7] Clean up pom.xml WE2-860 Signed-off-by: Mart Somermaa --- pom.xml | 32 ++++--------------- .../webeid/example/web/WelcomeController.java | 5 +-- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/pom.xml b/pom.xml index 49761fe..e35b076 100644 --- a/pom.xml +++ b/pom.xml @@ -5,21 +5,21 @@ org.springframework.boot spring-boot-starter-parent - 3.1.9 + 3.2.4 - org.webeid.example + eu.webeid.example web-eid-springboot-example 3.0.0-SNAPSHOT web-eid-springboot-example - Example Spring Boot project that demonstrates how to use Web eID for authentication and digital + Example Spring Boot application that demonstrates how to use Web eID for authentication and digital signing 17 - 2.22.1 - 3.0.0 + 3.2.5 + 3.0.1 5.3.0 1.44 @@ -31,20 +31,12 @@ org.springframework.boot - spring-boot-starter-validation + spring-boot-starter-security org.springframework.boot spring-boot-starter-thymeleaf - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.security - spring-security-config - org.digidoc4j @@ -57,22 +49,10 @@ ${webeid.version} - - org.springframework.boot - spring-boot-devtools - true - - org.springframework.boot spring-boot-starter-test test - - - org.junit.vintage - junit-vintage-engine - - org.springframework.security diff --git a/src/main/java/eu/webeid/example/web/WelcomeController.java b/src/main/java/eu/webeid/example/web/WelcomeController.java index e61fcc2..2ebb763 100644 --- a/src/main/java/eu/webeid/example/web/WelcomeController.java +++ b/src/main/java/eu/webeid/example/web/WelcomeController.java @@ -29,8 +29,8 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; -import jakarta.validation.constraints.NotNull; import java.security.Principal; +import java.util.Objects; import static eu.webeid.example.security.AuthTokenDTOAuthenticationProvider.ROLE_USER; @@ -40,7 +40,8 @@ public class WelcomeController { @PreAuthorize("hasAuthority('" + ROLE_USER + "')") @GetMapping("welcome") - public String welcome(Model model, @NotNull Principal principal) { + public String welcome(Model model, Principal principal) { + Objects.requireNonNull(principal); LOG.info("Showing welcome page, logged in as principal={}", principal.getName()); model.addAttribute("principalName", principal.getName()); return "welcome"; From b2e607fba923e80db4d9b838ac3018d0ab98e949 Mon Sep 17 00:00:00 2001 From: Mart Somermaa Date: Fri, 5 Apr 2024 21:12:45 +0300 Subject: [PATCH 4/7] Make FileDTO Serializable, enable Thymeleaf cache in production, use Jackson ObjectWriter and other minor cleanup WE2-860 Signed-off-by: Mart Somermaa --- .../config/ApplicationConfiguration.java | 3 +- .../AuthTokenDTOAuthenticationProvider.java | 22 +++++++-------- .../WebEidAjaxLoginProcessingFilter.java | 23 +++++++-------- .../AjaxAuthenticationSuccessHandler.java | 28 ++++++++----------- .../example/service/SigningService.java | 2 +- .../webeid/example/service/dto/FileDTO.java | 3 +- .../example/web/rest/ChallengeController.java | 2 +- src/main/resources/application-prod.yaml | 3 ++ .../eu/webeid/example/WebApplicationTest.java | 2 +- 9 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java b/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java index 5e974e4..cdbe016 100644 --- a/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java +++ b/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java @@ -31,6 +31,7 @@ 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.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; @@ -65,7 +66,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .addFilterBefore(new WebEidAjaxLoginProcessingFilter("/auth/login", manager, securityContextRepository), UsernamePasswordAuthenticationFilter.class) .logout(logout -> logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())) - .headers(headers -> headers.frameOptions(options -> options.sameOrigin())) + .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)) .build(); } diff --git a/src/main/java/eu/webeid/example/security/AuthTokenDTOAuthenticationProvider.java b/src/main/java/eu/webeid/example/security/AuthTokenDTOAuthenticationProvider.java index 03e535f..9965ff3 100644 --- a/src/main/java/eu/webeid/example/security/AuthTokenDTOAuthenticationProvider.java +++ b/src/main/java/eu/webeid/example/security/AuthTokenDTOAuthenticationProvider.java @@ -23,9 +23,12 @@ package eu.webeid.example.security; import eu.webeid.example.security.dto.AuthTokenDTO; +import eu.webeid.security.authtoken.WebEidAuthToken; +import eu.webeid.security.challenge.ChallengeNonceStore; +import eu.webeid.security.exceptions.AuthTokenException; +import eu.webeid.security.validator.AuthTokenValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.Authentication; @@ -34,15 +37,9 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.stereotype.Component; -import eu.webeid.security.authtoken.WebEidAuthToken; -import eu.webeid.security.challenge.ChallengeNonceStore; -import eu.webeid.security.exceptions.AuthTokenException; -import eu.webeid.security.validator.AuthTokenValidator; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -56,10 +53,13 @@ public class AuthTokenDTOAuthenticationProvider implements AuthenticationProvide private static final Logger LOG = LoggerFactory.getLogger(AuthTokenDTOAuthenticationProvider.class); - @Autowired - private AuthTokenValidator tokenValidator; - @Autowired - private ChallengeNonceStore challengeNonceStore; + private final AuthTokenValidator tokenValidator; + private final ChallengeNonceStore challengeNonceStore; + + public AuthTokenDTOAuthenticationProvider(AuthTokenValidator tokenValidator, ChallengeNonceStore challengeNonceStore) { + this.tokenValidator = tokenValidator; + this.challengeNonceStore = challengeNonceStore; + } @Override public Authentication authenticate(Authentication auth) throws AuthenticationException { diff --git a/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java b/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java index eb690d8..2b4f0cf 100644 --- a/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java +++ b/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java @@ -23,15 +23,14 @@ package eu.webeid.example.security; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - +import com.fasterxml.jackson.databind.ObjectReader; import eu.webeid.example.security.ajax.AjaxAuthenticationFailureHandler; import eu.webeid.example.security.ajax.AjaxAuthenticationSuccessHandler; import eu.webeid.example.security.dto.AuthTokenDTO; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; @@ -45,14 +44,17 @@ import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy; import org.springframework.security.web.context.SecurityContextRepository; +import java.io.IOException; + public class WebEidAjaxLoginProcessingFilter extends AbstractAuthenticationProcessingFilter { private static final Logger LOG = LoggerFactory.getLogger(WebEidAjaxLoginProcessingFilter.class); + private final ObjectReader OBJECT_READER = new ObjectMapper().readerFor(AuthTokenDTO.class); private final SecurityContextRepository securityContextRepository; public WebEidAjaxLoginProcessingFilter( - String defaultFilterProcessesUrl, - AuthenticationManager authenticationManager, - SecurityContextRepository securityContextRepository + String defaultFilterProcessesUrl, + AuthenticationManager authenticationManager, + SecurityContextRepository securityContextRepository ) { super(defaultFilterProcessesUrl); this.setAuthenticationManager(authenticationManager); @@ -64,7 +66,7 @@ public WebEidAjaxLoginProcessingFilter( @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) - throws AuthenticationException, IOException { + throws AuthenticationException, IOException { if (!HttpMethod.POST.name().equals(request.getMethod())) { LOG.warn("HttpMethod not supported: {}", request.getMethod()); throw new AuthenticationServiceException("HttpMethod not supported: " + request.getMethod()); @@ -76,8 +78,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ } LOG.info("attemptAuthentication(): Reading request body"); - final ObjectMapper objectMapper = new ObjectMapper(); - final AuthTokenDTO authTokenDTO = objectMapper.readValue(request.getReader(), AuthTokenDTO.class); + final AuthTokenDTO authTokenDTO = OBJECT_READER.readValue(request.getReader()); LOG.info("attemptAuthentication(): Creating token"); final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(null, authTokenDTO); LOG.info("attemptAuthentication(): Calling authentication manager"); @@ -86,7 +87,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { - super.successfulAuthentication(request, response, chain, authResult); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/OverriddenMethodBody + super.successfulAuthentication(request, response, chain, authResult); securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response); } } diff --git a/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java b/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java index b7b70b9..b545422 100644 --- a/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java +++ b/src/main/java/eu/webeid/example/security/ajax/AjaxAuthenticationSuccessHandler.java @@ -25,19 +25,17 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; +import com.fasterxml.jackson.databind.ObjectWriter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; +import java.io.IOException; + /** * Write custom response on having user successfully authenticated. *

    @@ -50,11 +48,11 @@ public class AjaxAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuc @Override public void onAuthenticationSuccess( - HttpServletRequest request, - HttpServletResponse response, - Authentication authentication + HttpServletRequest request, + HttpServletResponse response, + Authentication authentication ) - throws IOException { + throws IOException { LOG.info("onAuthenticationSuccess(): {}", authentication); response.setStatus(HttpServletResponse.SC_OK); @@ -64,23 +62,19 @@ public void onAuthenticationSuccess( } public static class AuthSuccessDTO { - private final ObjectMapper objectMapper = new ObjectMapper(); + private static final ObjectWriter OBJECT_WRITER = new ObjectMapper().writerFor(AuthSuccessDTO.class); @JsonProperty("sub") private String sub; @JsonProperty("auth") - private List auth; + private String auth; public static String asJson(Authentication authentication) throws JsonProcessingException { final AuthSuccessDTO dto = new AuthSuccessDTO(); dto.sub = authentication.getName(); - dto.auth = convertAuthorities(authentication.getAuthorities()); - return dto.objectMapper.writeValueAsString(dto); - } - - private static List convertAuthorities(Collection authorities) { - return authorities.stream().map(GrantedAuthority::toString).collect(Collectors.toList()); + dto.auth = authentication.getAuthorities().toString(); + return OBJECT_WRITER.writeValueAsString(dto); } } } diff --git a/src/main/java/eu/webeid/example/service/SigningService.java b/src/main/java/eu/webeid/example/service/SigningService.java index 69adc1c..b89835f 100644 --- a/src/main/java/eu/webeid/example/service/SigningService.java +++ b/src/main/java/eu/webeid/example/service/SigningService.java @@ -64,7 +64,7 @@ public class SigningService { private static final Logger LOG = LoggerFactory.getLogger(SigningService.class); private final Configuration signingConfiguration; - ObjectFactory httpSessionFactory; + private final ObjectFactory httpSessionFactory; public SigningService(ObjectFactory httpSessionFactory, YAMLConfig yamlConfig) { this.httpSessionFactory = httpSessionFactory; diff --git a/src/main/java/eu/webeid/example/service/dto/FileDTO.java b/src/main/java/eu/webeid/example/service/dto/FileDTO.java index dca653b..3a65edc 100644 --- a/src/main/java/eu/webeid/example/service/dto/FileDTO.java +++ b/src/main/java/eu/webeid/example/service/dto/FileDTO.java @@ -27,12 +27,13 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.io.Serializable; import java.net.URI; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Objects; -public class FileDTO { +public class FileDTO implements Serializable { private static final String EXAMPLE_FILENAME = "example-for-signing.txt"; private final String name; diff --git a/src/main/java/eu/webeid/example/web/rest/ChallengeController.java b/src/main/java/eu/webeid/example/web/rest/ChallengeController.java index 9640fe6..ecc3ee4 100644 --- a/src/main/java/eu/webeid/example/web/rest/ChallengeController.java +++ b/src/main/java/eu/webeid/example/web/rest/ChallengeController.java @@ -23,10 +23,10 @@ package eu.webeid.example.web.rest; import eu.webeid.example.service.dto.ChallengeDTO; +import eu.webeid.security.challenge.ChallengeNonceGenerator; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import eu.webeid.security.challenge.ChallengeNonceGenerator; @RestController @RequestMapping("auth") diff --git a/src/main/resources/application-prod.yaml b/src/main/resources/application-prod.yaml index 709d314..3868f35 100644 --- a/src/main/resources/application-prod.yaml +++ b/src/main/resources/application-prod.yaml @@ -3,3 +3,6 @@ web-eid-auth-token: use-digidoc4j-prod-configuration: true local-origin: "https://web-eid.eu" truststore-password: "changeit" +spring: + thymeleaf: + cache: true diff --git a/src/test/java/eu/webeid/example/WebApplicationTest.java b/src/test/java/eu/webeid/example/WebApplicationTest.java index d6e343b..e28e8fa 100644 --- a/src/test/java/eu/webeid/example/WebApplicationTest.java +++ b/src/test/java/eu/webeid/example/WebApplicationTest.java @@ -110,7 +110,7 @@ public void validateOcspResponse(XadesSignature xadesSignature) { MvcResult result = HttpHelper.login(mvcBuilder, session, ObjectMother.mockAuthToken()); session = (MockHttpSession) result.getRequest().getSession(); MockHttpServletResponse response = result.getResponse(); - assertEquals("{\"sub\":\"JAAK-KRISTJAN JÕEORG\",\"auth\":[\"ROLE_USER\"]}", response.getContentAsString()); + assertEquals("{\"sub\":\"JAAK-KRISTJAN JÕEORG\",\"auth\":\"[ROLE_USER]\"}", response.getContentAsString()); /* Example how to test file upload. response = HttpHelper.upload(mvcBuilder, session, mockMultipartFile()); From 97e2ba4900b9b84b107e7b563dc2d925bf678033 Mon Sep 17 00:00:00 2001 From: Mart Somermaa Date: Fri, 5 Apr 2024 21:13:55 +0300 Subject: [PATCH 5/7] Secure endpoints and services that require authentication WE2-860 Signed-off-by: Mart Somermaa --- .../example/config/ApplicationConfiguration.java | 2 +- .../java/eu/webeid/example/service/SigningService.java | 8 ++++++-- .../java/eu/webeid/example/web/WelcomeController.java | 4 ++-- .../eu/webeid/example/web/rest/SigningController.java | 10 +++++++++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java b/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java index cdbe016..343933f 100644 --- a/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java +++ b/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java @@ -42,7 +42,7 @@ @Configuration @EnableWebSecurity -@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true) +@EnableMethodSecurity(securedEnabled = true) public class ApplicationConfiguration implements WebMvcConfigurer { final AuthTokenDTOAuthenticationProvider authTokenDTOAuthenticationProvider; final SecurityContextRepository securityContextRepository; diff --git a/src/main/java/eu/webeid/example/service/SigningService.java b/src/main/java/eu/webeid/example/service/SigningService.java index b89835f..e96af33 100644 --- a/src/main/java/eu/webeid/example/service/SigningService.java +++ b/src/main/java/eu/webeid/example/service/SigningService.java @@ -29,6 +29,8 @@ import eu.webeid.example.service.dto.FileDTO; import eu.webeid.example.service.dto.SignatureDTO; import eu.webeid.security.certificate.CertificateData; +import jakarta.servlet.http.HttpSession; +import jakarta.xml.bind.DatatypeConverter; import org.apache.commons.io.FilenameUtils; import org.digidoc4j.Configuration; import org.digidoc4j.Container; @@ -44,10 +46,9 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ObjectFactory; import org.springframework.core.io.ByteArrayResource; +import org.springframework.security.access.annotation.Secured; import org.springframework.stereotype.Service; -import jakarta.servlet.http.HttpSession; -import jakarta.xml.bind.DatatypeConverter; import java.io.IOException; import java.io.InputStream; import java.security.NoSuchAlgorithmException; @@ -55,7 +56,10 @@ import java.security.cert.X509Certificate; import java.util.Objects; +import static eu.webeid.example.security.AuthTokenDTOAuthenticationProvider.ROLE_USER; + @Service +@Secured(ROLE_USER) public class SigningService { private static final String SESSION_ATTR_FILE = "file-to-sign"; diff --git a/src/main/java/eu/webeid/example/web/WelcomeController.java b/src/main/java/eu/webeid/example/web/WelcomeController.java index 2ebb763..0db6fc7 100644 --- a/src/main/java/eu/webeid/example/web/WelcomeController.java +++ b/src/main/java/eu/webeid/example/web/WelcomeController.java @@ -24,7 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.access.annotation.Secured; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -35,10 +35,10 @@ import static eu.webeid.example.security.AuthTokenDTOAuthenticationProvider.ROLE_USER; @Controller +@Secured(ROLE_USER) public class WelcomeController { private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class); - @PreAuthorize("hasAuthority('" + ROLE_USER + "')") @GetMapping("welcome") public String welcome(Model model, Principal principal) { Objects.requireNonNull(principal); diff --git a/src/main/java/eu/webeid/example/web/rest/SigningController.java b/src/main/java/eu/webeid/example/web/rest/SigningController.java index 14ecfae..4f935be 100644 --- a/src/main/java/eu/webeid/example/web/rest/SigningController.java +++ b/src/main/java/eu/webeid/example/web/rest/SigningController.java @@ -32,14 +32,22 @@ import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import static eu.webeid.example.security.AuthTokenDTOAuthenticationProvider.ROLE_USER; + @RestController @RequestMapping("sign") +@Secured(ROLE_USER) public class SigningController { private final SigningService signingService; From 689a30587e9f452623ef51cbc1737a479256029f Mon Sep 17 00:00:00 2001 From: Mart Somermaa Date: Fri, 5 Apr 2024 21:19:24 +0300 Subject: [PATCH 6/7] Override equals() and hashCode() in WebEidAuthentication WE2-860 Signed-off-by: Mart Somermaa --- .../webeid/example/security/WebEidAuthentication.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/eu/webeid/example/security/WebEidAuthentication.java b/src/main/java/eu/webeid/example/security/WebEidAuthentication.java index 4a67020..59ab2a7 100644 --- a/src/main/java/eu/webeid/example/security/WebEidAuthentication.java +++ b/src/main/java/eu/webeid/example/security/WebEidAuthentication.java @@ -61,4 +61,15 @@ private static String getPrincipalNameFromCertificate(X509Certificate userCertif } } + @Override + public boolean equals(Object o) { + if (!super.equals(o)) return false; + WebEidAuthentication that = (WebEidAuthentication) o; + return Objects.equals(idCode, that.idCode); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), idCode); + } } From 7e17821d1303059001328ea52db699285054baff Mon Sep 17 00:00:00 2001 From: Mart Somermaa Date: Fri, 5 Apr 2024 22:02:53 +0300 Subject: [PATCH 7/7] Use method injection to provide AuthTokenDTOAuthenticationProvider and AuthenticationConfiguration to filterChain(), move HttpSessionSecurityContextRepository creation into WebEidAjaxLoginProcessingFilter constructor, update README WE2-860 Signed-off-by: Mart Somermaa --- README.md | 4 ++-- .../config/ApplicationConfiguration.java | 21 ++----------------- .../WebEidAjaxLoginProcessingFilter.java | 6 +++--- .../WebEidAjaxLoginProcessingFilterTest.java | 8 +++---- 4 files changed, 10 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 008357d..5ab2297 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Web eID only works over a HTTPS connection with a trusted HTTPS certificate. You can either setup a reverse HTTPS proxy during development or, alternatively, configure HTTPS support directly in the bundled web server. HTTPS configuration is described in more detail in section _[HTTPS support](#https-support)_ below. -You can use, for example, [_ngrok_](https://ngrok.com/) to get a reverse HTTPS proxy. Download _ngrok_ and run it in a terminal window by providing the protocol and Spring Boot application port arguments as follows: +You can use, for example, [_ngrok_](https://ngrok.com/) or [_localtunnel_](https://theboroer.github.io/localtunnel-www/) to get a reverse HTTPS proxy. Download _ngrok_ and run it in a terminal window by providing the protocol and Spring Boot application port arguments as follows: ngrok http 8080 @@ -35,7 +35,7 @@ web-eid-auth-token: ### 3. Configure the trusted certificate authority certificates -The algorithm, which performs the validation of the Web eID authentication token, needs to know which intermediate certificate authorities (CA) are trusted to issue the eID authentication certificates. CA certificates are loaded either from `.cer` files in the profile-specific subdirectory of the [`certs`resource directory](src/main/resources/certs) or the [truststore file](src/main/resources/certs/prod/trusted_certificates.jks). By default, Estonian eID test CA certificates are included in the `dev` profile and production CA certificates in the `prod` profile. +The algorithm, which performs the validation of the Web eID authentication token, needs to know which intermediate certificate authorities (CA) are trusted to issue the eID authentication certificates. CA certificates are loaded either from `.cer` files in the profile-specific subdirectory of the [`certs` resource directory](src/main/resources/certs) or the [truststore file](src/main/resources/certs/prod/trusted_certificates.jks). By default, Estonian eID test CA certificates are included in the `dev` profile and production CA certificates in the `prod` profile. In case you need to provide your own CA certificates, either add the `.cer` files to the `src/main/resources/certs/{dev,prod}` profile-specific directory or add the certificates to the truststore file. diff --git a/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java b/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java index 343933f..d93c942 100644 --- a/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java +++ b/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java @@ -26,7 +26,6 @@ import eu.webeid.example.security.WebEidAjaxLoginProcessingFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -35,8 +34,6 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; -import org.springframework.security.web.context.HttpSessionSecurityContextRepository; -import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -44,26 +41,12 @@ @EnableWebSecurity @EnableMethodSecurity(securedEnabled = true) public class ApplicationConfiguration implements WebMvcConfigurer { - final AuthTokenDTOAuthenticationProvider authTokenDTOAuthenticationProvider; - final SecurityContextRepository securityContextRepository; - - public ApplicationConfiguration(AuthTokenDTOAuthenticationProvider authTokenDTOAuthenticationProvider) { - this.authTokenDTOAuthenticationProvider = authTokenDTOAuthenticationProvider; - this.securityContextRepository = new HttpSessionSecurityContextRepository(); - } - - @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { - return authenticationConfiguration.getAuthenticationManager(); - } @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - AuthenticationManager manager = authenticationManager(http.getSharedObject(AuthenticationConfiguration.class)); - + public SecurityFilterChain filterChain(HttpSecurity http, AuthTokenDTOAuthenticationProvider authTokenDTOAuthenticationProvider, AuthenticationConfiguration authConfig) throws Exception { return http .authenticationProvider(authTokenDTOAuthenticationProvider) - .addFilterBefore(new WebEidAjaxLoginProcessingFilter("/auth/login", manager, securityContextRepository), + .addFilterBefore(new WebEidAjaxLoginProcessingFilter("/auth/login", authConfig.getAuthenticationManager()), UsernamePasswordAuthenticationFilter.class) .logout(logout -> logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())) .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)) diff --git a/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java b/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java index 2b4f0cf..cc47f86 100644 --- a/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java +++ b/src/main/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilter.java @@ -42,6 +42,7 @@ import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.SecurityContextRepository; import java.io.IOException; @@ -53,15 +54,14 @@ public class WebEidAjaxLoginProcessingFilter extends AbstractAuthenticationProce public WebEidAjaxLoginProcessingFilter( String defaultFilterProcessesUrl, - AuthenticationManager authenticationManager, - SecurityContextRepository securityContextRepository + AuthenticationManager authenticationManager ) { super(defaultFilterProcessesUrl); this.setAuthenticationManager(authenticationManager); this.setAuthenticationSuccessHandler(new AjaxAuthenticationSuccessHandler()); this.setAuthenticationFailureHandler(new AjaxAuthenticationFailureHandler()); setSessionAuthenticationStrategy(new SessionFixationProtectionStrategy()); - this.securityContextRepository = securityContextRepository; + this.securityContextRepository = new HttpSessionSecurityContextRepository(); } @Override diff --git a/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java b/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java index adbaff5..cb95073 100644 --- a/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java +++ b/src/test/java/eu/webeid/example/security/WebEidAjaxLoginProcessingFilterTest.java @@ -1,18 +1,17 @@ package eu.webeid.example.security; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.Test; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.StringReader; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import org.springframework.security.web.context.SecurityContextRepository; class WebEidAjaxLoginProcessingFilterTest { @@ -32,10 +31,9 @@ void testAttemptAuthentication() throws Exception { when(request.getReader()).thenReturn(new BufferedReader(new StringReader(AUTH_TOKEN))); final AuthenticationManager authenticationManager = mock(AuthenticationManager.class); - final SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class); assertDoesNotThrow(() -> - new WebEidAjaxLoginProcessingFilter("/auth/login", authenticationManager, securityContextRepository) + new WebEidAjaxLoginProcessingFilter("/auth/login", authenticationManager) .attemptAuthentication(request, response)); } } \ No newline at end of file