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