From f21ea47ea6229a22eaf727a4021e0d1dbb64ea74 Mon Sep 17 00:00:00 2001 From: Baptiste Toulemonde <toulemonde@cines.fr> Date: Wed, 24 Nov 2021 10:34:18 +0100 Subject: [PATCH] wip --- pom.xml | 6 +- .../config/InMemoryRequestRepository.java | 43 ++++++++ .../SmartHarvesterSecurityConfiguration.java | 104 +++++++++--------- .../smartharvester/config/TokenFilter.java | 41 +++++++ .../smartharvester/config/TokenStorage.java | 25 +++++ .../SmartHarvesterUserController.java | 6 + src/main/resources/application.properties | 11 +- version | 2 +- 8 files changed, 185 insertions(+), 53 deletions(-) create mode 100644 src/main/java/com/smartharvester/config/InMemoryRequestRepository.java create mode 100644 src/main/java/com/smartharvester/config/TokenFilter.java create mode 100644 src/main/java/com/smartharvester/config/TokenStorage.java diff --git a/pom.xml b/pom.xml index e7d2736..1568ab7 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> - + <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> @@ -115,6 +115,10 @@ <version>0.0.20131108.vaadin1</version> <scope>compile</scope> </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-oauth2-client</artifactId> + </dependency> </dependencies> <build> diff --git a/src/main/java/com/smartharvester/config/InMemoryRequestRepository.java b/src/main/java/com/smartharvester/config/InMemoryRequestRepository.java new file mode 100644 index 0000000..e314cdd --- /dev/null +++ b/src/main/java/com/smartharvester/config/InMemoryRequestRepository.java @@ -0,0 +1,43 @@ +package com.smartharvester.config; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; + + +public class InMemoryRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> { + + private final Map< String, OAuth2AuthorizationRequest > cache = new HashMap<>(); + + @Override + public OAuth2AuthorizationRequest loadAuthorizationRequest( HttpServletRequest request ) { + String state = request.getParameter( "state" ); + if ( state != null ) { + return removeAuthorizationRequest( request ); + } + return null; + } + + @Override + public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request, + HttpServletResponse response) { + String state = authorizationRequest.getState(); + cache.put( state, authorizationRequest ); + } + + @Override + public OAuth2AuthorizationRequest removeAuthorizationRequest( HttpServletRequest request ) { + String state = request.getParameter( "state" ); + if ( state != null ) { + return cache.remove( state ); + } + + return null; + } + +} diff --git a/src/main/java/com/smartharvester/config/SmartHarvesterSecurityConfiguration.java b/src/main/java/com/smartharvester/config/SmartHarvesterSecurityConfiguration.java index 614245e..a729bbf 100644 --- a/src/main/java/com/smartharvester/config/SmartHarvesterSecurityConfiguration.java +++ b/src/main/java/com/smartharvester/config/SmartHarvesterSecurityConfiguration.java @@ -1,73 +1,77 @@ package com.smartharvester.config; -import com.smartharvester.security.jwt.AuthEntryPointJwt; -import com.smartharvester.security.jwt.AuthTokenFilter; -import com.smartharvester.security.services.UserServiceImpl; +import java.io.IOException; +import java.util.Collections; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; - -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import com.fasterxml.jackson.databind.ObjectMapper; @Configuration @EnableWebSecurity -@Order(1) -public class SmartHarvesterSecurityConfiguration extends WebSecurityConfigurerAdapter { - - @Autowired - UserServiceImpl userDetailsService; - - @Autowired - private AuthEntryPointJwt unauthorizedHandler; +public class SmartHarvesterSecurityConfiguration extends WebSecurityConfigurerAdapter { - @Bean - public AuthTokenFilter authenticationJwtTokenFilter() { - return new AuthTokenFilter(); - } + private final ObjectMapper mapper; + private final TokenStorage tokenStorage; + private final TokenFilter tokenFilter; - @Override - public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { - authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); - } + public SmartHarvesterSecurityConfiguration(ObjectMapper mapper, TokenStorage tokenStorage, TokenFilter tokenFilter) { + this.mapper = mapper; + this.tokenStorage = tokenStorage; + this.tokenFilter = tokenFilter; + } - @Bean - @Override - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } + @Override + protected void configure(HttpSecurity http) throws Exception { + http.cors().and().authorizeRequests().antMatchers("/oauth2/**", "/login**").permitAll() + .anyRequest().authenticated() + .and() + .oauth2Login().authorizationEndpoint().authorizationRequestRepository(new InMemoryRequestRepository()) + .and() + .successHandler(this::successHandler) + .and() + .exceptionHandling().authenticationEntryPoint(this::authenticationEntryPoint); + + http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class); + } + + @Bean + public CorsConfigurationSource corsConfiguration() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedMethods( Collections.singletonList( "*" ) ); + config.setAllowedOrigins( Collections.singletonList( "*" ) ); + config.setAllowedHeaders( Collections.singletonList( "*" ) ); - @Override - protected void configure(HttpSecurity http) throws Exception { - http.cors().and().csrf().disable() - .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() - .authorizeRequests().antMatchers("/harvester/auth/**").permitAll().and() - .authorizeRequests().antMatchers("/harvester/api/catalogs").permitAll().and() - .authorizeRequests().antMatchers("/resources/**", "/public/error/css/**").permitAll() - .antMatchers("/harvester/api/**").authenticated() - .antMatchers("/public/error/css/**").permitAll().and() - .logout().logoutUrl("/harvester/auth/logout").logoutSuccessHandler((req, resp, auth) -> resp.setStatus(HttpServletResponse.SC_OK)); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration( "/**", config ); + return source; + } - http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); - } + private void successHandler(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException { + String token = this.tokenStorage.generateToken(authentication); + response.getWriter().write(mapper.writeValueAsString(Collections.singletonMap("accessToken", token))); + } - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } + private void authenticationEntryPoint(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authenticationException) throws IOException { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter() + .write(this.mapper.writeValueAsString(Collections.singletonMap("error", "Unauthenticated"))); + } } diff --git a/src/main/java/com/smartharvester/config/TokenFilter.java b/src/main/java/com/smartharvester/config/TokenFilter.java new file mode 100644 index 0000000..c62fd56 --- /dev/null +++ b/src/main/java/com/smartharvester/config/TokenFilter.java @@ -0,0 +1,41 @@ +package com.smartharvester.config; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import ch.qos.logback.core.subst.Token; + +@Component +public class TokenFilter extends OncePerRequestFilter{ + + private final TokenStorage tokenStorage; + + public TokenFilter(TokenStorage tokenStorage) { + this.tokenStorage = tokenStorage; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + String authToken = request.getHeader( "Authorization" ); + if ( authToken != null ) { + String token = authToken.split( " " )[ 1 ]; + Authentication authentication = tokenStorage.getAuth( token ); + if ( authentication != null ) { + SecurityContextHolder.getContext().setAuthentication( authentication ); + } + } + + filterChain.doFilter( request, response ); + } + +} diff --git a/src/main/java/com/smartharvester/config/TokenStorage.java b/src/main/java/com/smartharvester/config/TokenStorage.java new file mode 100644 index 0000000..aa74f58 --- /dev/null +++ b/src/main/java/com/smartharvester/config/TokenStorage.java @@ -0,0 +1,25 @@ +package com.smartharvester.config; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +@Component +public class TokenStorage { + + private final Map<String, Authentication> cache = new HashMap(); + + public String generateToken(Authentication authentication) { + String token = UUID.randomUUID().toString(); + cache.put( token, authentication ); + return token; + } + + public Authentication getAuth(String token) { + return cache.getOrDefault( token, null ); + } + +} diff --git a/src/main/java/com/smartharvester/controller/SmartHarvesterUserController.java b/src/main/java/com/smartharvester/controller/SmartHarvesterUserController.java index 4875cb4..3de6794 100644 --- a/src/main/java/com/smartharvester/controller/SmartHarvesterUserController.java +++ b/src/main/java/com/smartharvester/controller/SmartHarvesterUserController.java @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import java.util.*; @@ -38,6 +39,11 @@ public class SmartHarvesterUserController { @Autowired private UserDaoSevice userService; + + @GetMapping("/username") + public String getUserName(@AuthenticationPrincipal(expression = "attributes['name]") String username) { + return username; + } /** * Method to fetch all users. diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index b02f4d5..3aadf4e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -40,4 +40,13 @@ server.error.whitelabel.enabled=false server.error.path=/error #9. F2DS Settings Filename -f2dp.settings.filename=/f2pconf/settings.json \ No newline at end of file +f2dp.settings.filename=/f2pconf/settings.json + +spring.security.oauth2.client.provider.oidc.issuer-uri=https://iam-pillar.cloud.cnaf.infn.it/.well-known/openid-configuration + +spring.security.oauth2.client.registration.oidc.client-id=a03a6ac2-acfe-4916-9d0f-db874ea94e75 +spring.security.oauth2.client.registration.oidc.client-secret=cLONCJ8MccdHwobCEMSl_sYDJGKpmBxH16SyiRIBx8XeoDa2ZLwzTvF_aVoEeOt3h2sNbZqltRqhfHKeI3g7Dw +spring.security.oauth2.client.registration.oidc.scope=address,phone,openid,email,profile +spring.security.oauth2.client.registration.oidc.redirect-uri=http://localhost:4200/callback + + diff --git a/version b/version index 04d0d54..2b26aed 100644 --- a/version +++ b/version @@ -1 +1 @@ -v3 \ No newline at end of file +v4 \ No newline at end of file -- GitLab