diff --git a/pluto-server/pom.xml b/pluto-server/pom.xml index 099275d17b573d74b86cd0de8dc179855a78cd1c..2c899e3df470f8b828b139db5a0dd41af2b6509d 100644 --- a/pluto-server/pom.xml +++ b/pluto-server/pom.xml @@ -168,6 +168,11 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-test</artifactId> + </dependency> + <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> diff --git a/pluto-server/src/main/java/de/itvsh/ozg/pluto/PlutoServerApplication.java b/pluto-server/src/main/java/de/itvsh/ozg/pluto/PlutoServerApplication.java index 77094c8371175c6bc0e073ae69d44156617284db..c3e9cbd217f58f9afa146e0d9a328eeecdc56016 100644 --- a/pluto-server/src/main/java/de/itvsh/ozg/pluto/PlutoServerApplication.java +++ b/pluto-server/src/main/java/de/itvsh/ozg/pluto/PlutoServerApplication.java @@ -5,9 +5,12 @@ import java.util.TimeZone; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.security.authentication.AuthenticationTrustResolver; +import org.springframework.security.authentication.AuthenticationTrustResolverImpl; import io.mongock.runner.springboot.EnableMongock; @@ -23,4 +26,9 @@ public class PlutoServerApplication { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); SpringApplication.run(PlutoServerApplication.class, args); } + + @Bean + public AuthenticationTrustResolver trustResolver() { + return new AuthenticationTrustResolverImpl(); + } } \ No newline at end of file diff --git a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CallContextHandleInterceptor.java b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CallContextHandleInterceptor.java index 6342d627cb95e251ffc0a859d07f2b4f10779bd8..68c6072e330399106361f4097f7f501e589096bc 100644 --- a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CallContextHandleInterceptor.java +++ b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CallContextHandleInterceptor.java @@ -94,12 +94,12 @@ class CallContextHandleInterceptor implements ServerInterceptor { CallContextUser createUser() { var builder = CallContextUser.builder() + .userId(getFromHeaders(KEY_USER_ID, headers)) .userName(getFromHeaders(KEY_USER_NAME, headers)) .organisatorischeEinheitenIds(getCollection(KEY_ACCESS_LIMITED_ORGAID, headers)); // TODO throw exception if missing required data as soon all clients are fine // with using headers for auth data. - getFromHeaders(KEY_USER_ID, headers).ifPresentOrElse(builder::userId, () -> LOG.warn("Missing user id in grpc header.")); getFromHeaders(KEY_CLIENT_NAME, headers).ifPresentOrElse(builder::clientName, () -> LOG.warn("Missing client name in grpc header.")); return builder.build(); diff --git a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CallContextUser.java b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CallContextUser.java index 7a37d14d040876302598bdb7596106710622ca67..6f1b4b5a090d8bdc87607e5e6a09b12f49d7f2aa 100644 --- a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CallContextUser.java +++ b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CallContextUser.java @@ -18,7 +18,8 @@ public class CallContextUser implements AuthenticatedPrincipal, Serializable { private final String clientName; - private final String userId; + @Builder.Default + private final transient Optional<String> userId = Optional.empty(); @Builder.Default private final transient Optional<String> userName = Optional.empty(); @Singular diff --git a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CurrentUserService.java b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CurrentUserService.java new file mode 100644 index 0000000000000000000000000000000000000000..d6b15332f824e8ce8d614931eed5a45ae7f87753 --- /dev/null +++ b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/callcontext/CurrentUserService.java @@ -0,0 +1,43 @@ +package de.itvsh.ozg.pluto.common.callcontext; + +import java.util.Optional; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationTrustResolver; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +@Service +public class CurrentUserService { + + @Autowired + private AuthenticationTrustResolver trustResolver; + + public CallContextUser getUser() { + var auth = getAuthentication(); + + if (auth instanceof CallContextAuthenticationToken) { + return (CallContextUser) auth.getPrincipal(); + } else { + return CallContextUser.builder() + .clientName(auth.getName()) + .organisatorischeEinheitenIds( + auth.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toUnmodifiableSet())) + .build(); + } + + } + + public Authentication getAuthentication() { + return findTrustedAuthentication().orElseThrow(() -> new IllegalStateException("No authenticated User found")); + } + + Optional<Authentication> findTrustedAuthentication() { + return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) + .filter(auth -> !trustResolver.isAnonymous(auth)) + .filter(Authentication::isAuthenticated); + } +} diff --git a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/callcontext/CallContextUserTestFactory.java b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/callcontext/CallContextUserTestFactory.java index 4aeb8415b4de1310e3495b9ae2d1bfb353aff8c9..68de875e14329f85c258c0d0261a1a47d166211c 100644 --- a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/callcontext/CallContextUserTestFactory.java +++ b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/callcontext/CallContextUserTestFactory.java @@ -13,7 +13,7 @@ public class CallContextUserTestFactory { static CallContextUser.CallContextUserBuilder createBuilder() { return CallContextUser.builder() .clientName(CallContextTestFactory.CLIENT) - .userId(UserTestFactory.ID) + .userId(Optional.of(UserTestFactory.ID)) .userName(Optional.of(UserTestFactory.NAME)) .organisatorischeEinheitenId(UserTestFactory.ORGANISATORISCHE_EINHEITEN_ID); } diff --git a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/callcontext/CurrentUserServiceITCase.java b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/callcontext/CurrentUserServiceITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..66bca90a40ff38ddf6842475528bf22c2edef3ee --- /dev/null +++ b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/callcontext/CurrentUserServiceITCase.java @@ -0,0 +1,47 @@ +package de.itvsh.ozg.pluto.common.callcontext; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; + +import de.itvsh.kop.common.test.ITCase; + +@ITCase +class CurrentUserServiceITCase { + + @Autowired + private CurrentUserService service; + + @Test + void shouldThrowExceptionIfUserIsMissing() { + assertThatThrownBy(() -> service.getUser()).isInstanceOf(IllegalStateException.class); + } + + @Test + @WithAnonymousUser + void shouldThrowExceptionIfAnonymouse() { + assertThatThrownBy(() -> service.getUser()).isInstanceOf(IllegalStateException.class); + } + + @Test + @WithMockUser + void shouldReturnUser() { + var user = service.getUser(); + + assertThat(user.getName()).isEqualTo("user"); + } + + @Test + void shouldReturnUserFromToken() { + var inUser = CallContextUserTestFactory.create(); + SecurityContextHolder.getContext().setAuthentication(CallContextAuthenticationToken.authenticate(inUser)); + + var user = service.getUser(); + + assertThat(user).isSameAs(inUser); + } +}