diff --git a/.github/workflows/.trivyignore b/.github/workflows/.trivyignore index c41ad4e..ac3a5c0 100644 --- a/.github/workflows/.trivyignore +++ b/.github/workflows/.trivyignore @@ -1,4 +1,9 @@ -# Date: Feb 27, 2024 -# Notes: Issue with libexpat, parsing large tokens can trigger a denial of service -# Needs to be fixed in Docker Image. -CVE-2023-52425 \ No newline at end of file +# Date: March 21, 2024 +# Issue: Vulnerability in spring-web +# Solution: Spring boot needs to update its version of spring +CVE-2024-22259 + +# Date: March 21, 2024 +# Issue: Vulnerability in spring-security-core +# Solution: Spring boot needs to update its version of spring +CVE-2024-22257 \ No newline at end of file diff --git a/src/main/java/eu/dissco/orchestration/backend/controller/SourceSystemController.java b/src/main/java/eu/dissco/orchestration/backend/controller/SourceSystemController.java index 8947b81..6faf1d0 100644 --- a/src/main/java/eu/dissco/orchestration/backend/controller/SourceSystemController.java +++ b/src/main/java/eu/dissco/orchestration/backend/controller/SourceSystemController.java @@ -11,8 +11,6 @@ import eu.dissco.orchestration.backend.exception.ProcessingFailedException; import eu.dissco.orchestration.backend.properties.ApplicationProperties; import eu.dissco.orchestration.backend.service.SourceSystemService; -import freemarker.template.TemplateException; -import io.kubernetes.client.openapi.ApiException; import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/eu/dissco/orchestration/backend/security/JwtAuthConverter.java b/src/main/java/eu/dissco/orchestration/backend/security/JwtAuthConverter.java index 5dab213..7f7f97f 100644 --- a/src/main/java/eu/dissco/orchestration/backend/security/JwtAuthConverter.java +++ b/src/main/java/eu/dissco/orchestration/backend/security/JwtAuthConverter.java @@ -2,33 +2,39 @@ import jakarta.validation.constraints.NotNull; import java.util.Collection; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import org.springframework.core.convert.converter.Converter; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtClaimNames; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; -import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; import org.springframework.stereotype.Component; @Component public class JwtAuthConverter implements Converter { - private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); - @Override public AbstractAuthenticationToken convert(@NotNull Jwt jwt) { - Collection authorities = - converterToStream(jwt).collect(Collectors.toSet()); - return new JwtAuthenticationToken(jwt, authorities, getPrincipalClaimName(jwt)); + return new JwtAuthenticationToken(jwt, extractRoles(jwt), getPrincipalClaimName(jwt)); } - private Stream converterToStream(Jwt jwt) { - return Optional.of(jwtGrantedAuthoritiesConverter.convert(jwt)).stream() - .flatMap(Collection::stream); + private Set extractRoles(Jwt jwt) { + Set authorities = new HashSet<>(); + if (jwt.getClaims().containsKey("resource_access")) { + ((Map) jwt.getClaims().get("resource_access")).forEach((k, v) -> { + Map resourceAccess = (Map) v; + resourceAccess.forEach((k1, v1) -> { + if (k1.equals("roles")) { + ((Collection) v1).forEach( + role -> authorities.add((GrantedAuthority) () -> "ROLE_" + role)); + } + }); + }); + } + return authorities; } private String getPrincipalClaimName(Jwt jwt) { diff --git a/src/main/java/eu/dissco/orchestration/backend/security/WebSecurityConfig.java b/src/main/java/eu/dissco/orchestration/backend/security/WebSecurityConfig.java index 55a786a..3244bcd 100644 --- a/src/main/java/eu/dissco/orchestration/backend/security/WebSecurityConfig.java +++ b/src/main/java/eu/dissco/orchestration/backend/security/WebSecurityConfig.java @@ -21,9 +21,12 @@ public class WebSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests - .requestMatchers(EndpointRequest.to(HealthEndpoint.class)).permitAll() - .requestMatchers(HttpMethod.GET, "**").permitAll() - .anyRequest().authenticated()); + .requestMatchers(EndpointRequest.to(HealthEndpoint.class)) + .permitAll() + .requestMatchers(HttpMethod.GET, "**") + .permitAll() + .anyRequest() + .hasRole("orchestration-admin")); http.oauth2ResourceServer(jwtoauth2ResourceServer -> jwtoauth2ResourceServer.jwt(( jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter) diff --git a/src/test/java/eu/dissco/orchestration/backend/security/JwtAuthConverterTest.java b/src/test/java/eu/dissco/orchestration/backend/security/JwtAuthConverterTest.java new file mode 100644 index 0000000..f683a0b --- /dev/null +++ b/src/test/java/eu/dissco/orchestration/backend/security/JwtAuthConverterTest.java @@ -0,0 +1,58 @@ +package eu.dissco.orchestration.backend.security; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Instant; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.oauth2.jwt.Jwt; + +@ExtendWith(MockitoExtension.class) +class JwtAuthConverterTest { + + private JwtAuthConverter converter; + + @BeforeEach + void setup() { + converter = new JwtAuthConverter(); + } + + @Test + void testEmptyToken() { + // Given + var jwt = new Jwt("SomeRandomStringWithTheFullValue", Instant.now(), Instant.now(), + Map.of("kid", "SomeRandom", "typ", "JWT", "alg", "RS256"), + Map.of("sub", "adf294ba-bb03-4962-8042-a37f1648458e")); + + // When + var token = converter.convert(jwt); + + // Then + assertThat(token.isAuthenticated()).isTrue(); + assertThat(token.getName()).isEqualTo("adf294ba-bb03-4962-8042-a37f1648458e"); + assertThat(token.getAuthorities()).isEmpty(); + } + + @Test + void testTokenRoles() { + // Given + var jwt = new Jwt("SomeRandomStringWithTheFullValue", Instant.now(), Instant.now(), + Map.of("kid", "SomeRandom", "typ", "JWT", "alg", "RS256"), + Map.of("sub", "adf294ba-bb03-4962-8042-a37f1648458e", + "resource_access", Map.of("orchestration-service", + Map.of("roles", List.of("orchestration-admin"))))); + + // When + var token = converter.convert(jwt); + + // Then + assertThat(token.isAuthenticated()).isTrue(); + assertThat(token.getName()).isEqualTo("adf294ba-bb03-4962-8042-a37f1648458e"); + assertThat(token.getAuthorities()).hasSize(1); + } + +}