diff --git a/pom.xml b/pom.xml index d66fd43d3..481a3cc62 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ scalecube-services-parent - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT pom ScaleCube-Services diff --git a/services-api/pom.xml b/services-api/pom.xml index 00235e089..a21e0a881 100644 --- a/services-api/pom.xml +++ b/services-api/pom.xml @@ -6,7 +6,7 @@ io.scalecube scalecube-services-parent - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT scalecube-services-api diff --git a/services/src/main/java/io/scalecube/services/routing/RandomServiceRouter.java b/services-api/src/main/java/io/scalecube/services/routing/RandomServiceRouter.java similarity index 100% rename from services/src/main/java/io/scalecube/services/routing/RandomServiceRouter.java rename to services-api/src/main/java/io/scalecube/services/routing/RandomServiceRouter.java diff --git a/services/src/main/java/io/scalecube/services/routing/RoundRobinServiceRouter.java b/services-api/src/main/java/io/scalecube/services/routing/RoundRobinServiceRouter.java similarity index 100% rename from services/src/main/java/io/scalecube/services/routing/RoundRobinServiceRouter.java rename to services-api/src/main/java/io/scalecube/services/routing/RoundRobinServiceRouter.java diff --git a/services-gateway/src/main/java/io/scalecube/services/gateway/client/StaticAddressRouter.java b/services-api/src/main/java/io/scalecube/services/routing/StaticAddressRouter.java similarity index 89% rename from services-gateway/src/main/java/io/scalecube/services/gateway/client/StaticAddressRouter.java rename to services-api/src/main/java/io/scalecube/services/routing/StaticAddressRouter.java index e4216b536..6d9bfd314 100644 --- a/services-gateway/src/main/java/io/scalecube/services/gateway/client/StaticAddressRouter.java +++ b/services-api/src/main/java/io/scalecube/services/routing/StaticAddressRouter.java @@ -1,4 +1,4 @@ -package io.scalecube.services.gateway.client; +package io.scalecube.services.routing; import io.scalecube.services.Address; import io.scalecube.services.ServiceEndpoint; @@ -7,7 +7,6 @@ import io.scalecube.services.ServiceRegistration; import io.scalecube.services.api.ServiceMessage; import io.scalecube.services.registry.api.ServiceRegistry; -import io.scalecube.services.routing.Router; import java.util.Collections; import java.util.Optional; import java.util.UUID; @@ -16,7 +15,7 @@ * Syntethic router for returning pre-constructed {@link ServiceReference} instance with given * address. */ -public final class StaticAddressRouter implements Router { +public class StaticAddressRouter implements Router { private final ServiceReference serviceReference; diff --git a/services-discovery/pom.xml b/services-discovery/pom.xml index ef864fa37..2e8d2fc7b 100644 --- a/services-discovery/pom.xml +++ b/services-discovery/pom.xml @@ -4,7 +4,7 @@ io.scalecube scalecube-services-parent - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT scalecube-services-discovery diff --git a/services-examples/pom.xml b/services-examples/pom.xml index b9644963b..6fb0c8e3f 100644 --- a/services-examples/pom.xml +++ b/services-examples/pom.xml @@ -5,7 +5,7 @@ io.scalecube scalecube-services-parent - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT scalecube-services-examples diff --git a/services-gateway/pom.xml b/services-gateway/pom.xml index dd5130bda..a3c52118f 100644 --- a/services-gateway/pom.xml +++ b/services-gateway/pom.xml @@ -5,7 +5,7 @@ io.scalecube scalecube-services-parent - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT scalecube-services-gateway diff --git a/services-gateway/src/main/java/io/scalecube/services/gateway/http/HttpGatewayAcceptor.java b/services-gateway/src/main/java/io/scalecube/services/gateway/http/HttpGatewayAcceptor.java index 8493fd1c3..86d71cfcb 100644 --- a/services-gateway/src/main/java/io/scalecube/services/gateway/http/HttpGatewayAcceptor.java +++ b/services-gateway/src/main/java/io/scalecube/services/gateway/http/HttpGatewayAcceptor.java @@ -21,8 +21,8 @@ import io.scalecube.services.exceptions.ServiceException; import io.scalecube.services.exceptions.ServiceProviderErrorMapper; import io.scalecube.services.gateway.ReferenceCountUtil; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.registry.api.ServiceRegistry; +import io.scalecube.services.routing.StaticAddressRouter; import io.scalecube.services.transport.api.DataCodec; import java.util.List; import java.util.function.BiFunction; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/files/FileDownloadTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/files/FileDownloadTest.java index c28aa5da9..0b08bafa3 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/files/FileDownloadTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/files/FileDownloadTest.java @@ -20,10 +20,10 @@ import io.scalecube.services.exceptions.InternalServiceException; import io.scalecube.services.exceptions.ServiceUnavailableException; import io.scalecube.services.files.FileServiceImpl; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport; import io.scalecube.services.gateway.http.HttpGateway; import io.scalecube.services.gateway.websocket.WebsocketGateway; +import io.scalecube.services.routing.StaticAddressRouter; import io.scalecube.services.transport.api.ServiceTransport.CredentialsSupplier; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpClientConnectionTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpClientConnectionTest.java index 32dcbfb8f..22ff59ec0 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpClientConnectionTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpClientConnectionTest.java @@ -9,8 +9,8 @@ import io.scalecube.services.annotations.Service; import io.scalecube.services.annotations.ServiceMethod; import io.scalecube.services.discovery.ScalecubeServiceDiscovery; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.http.HttpGatewayClientTransport; +import io.scalecube.services.routing.StaticAddressRouter; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; import java.io.IOException; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpGatewayTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpGatewayTest.java index ccc72004f..8d000838b 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpGatewayTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpGatewayTest.java @@ -20,8 +20,8 @@ import io.scalecube.services.gateway.ErrorService; import io.scalecube.services.gateway.ErrorServiceImpl; import io.scalecube.services.gateway.SomeException; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.http.HttpGatewayClientTransport; +import io.scalecube.services.routing.StaticAddressRouter; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; import java.time.Duration; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpLocalGatewayTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpLocalGatewayTest.java index 88a58c0b9..4e5e15103 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpLocalGatewayTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpLocalGatewayTest.java @@ -19,8 +19,8 @@ import io.scalecube.services.gateway.ErrorService; import io.scalecube.services.gateway.ErrorServiceImpl; import io.scalecube.services.gateway.SomeException; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.http.HttpGatewayClientTransport; +import io.scalecube.services.routing.StaticAddressRouter; import java.time.Duration; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/rest/RestGatewayTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/rest/RestGatewayTest.java index c70936656..1ff2f801d 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/rest/RestGatewayTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/rest/RestGatewayTest.java @@ -19,10 +19,10 @@ import io.scalecube.services.discovery.ScalecubeServiceDiscovery; import io.scalecube.services.examples.GreetingServiceImpl; import io.scalecube.services.exceptions.ServiceUnavailableException; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport.Builder; import io.scalecube.services.gateway.http.HttpGateway; import io.scalecube.services.gateway.websocket.WebsocketGateway; +import io.scalecube.services.routing.StaticAddressRouter; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; import java.net.URI; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketClientConnectionTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketClientConnectionTest.java index 90fd56300..45945a0a9 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketClientConnectionTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketClientConnectionTest.java @@ -10,8 +10,8 @@ import io.scalecube.services.Microservices.Context; import io.scalecube.services.ServiceCall; import io.scalecube.services.discovery.ScalecubeServiceDiscovery; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport; +import io.scalecube.services.routing.StaticAddressRouter; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; import java.io.IOException; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketClientTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketClientTest.java index 216c31342..24cab60dd 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketClientTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketClientTest.java @@ -13,8 +13,8 @@ import io.scalecube.services.gateway.ErrorService; import io.scalecube.services.gateway.ErrorServiceImpl; import io.scalecube.services.gateway.SomeException; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport; +import io.scalecube.services.routing.StaticAddressRouter; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; import java.time.Duration; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayAuthTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayAuthTest.java index aec806765..d8368430e 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayAuthTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayAuthTest.java @@ -14,8 +14,8 @@ import io.scalecube.services.gateway.AuthRegistry; import io.scalecube.services.gateway.SecuredService; import io.scalecube.services.gateway.SecuredServiceImpl; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport; +import io.scalecube.services.routing.StaticAddressRouter; import java.time.Duration; import java.util.Collections; import java.util.HashSet; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayTest.java index 1f0aa715b..f86083d71 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayTest.java @@ -21,8 +21,8 @@ import io.scalecube.services.gateway.ErrorService; import io.scalecube.services.gateway.ErrorServiceImpl; import io.scalecube.services.gateway.SomeException; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport; +import io.scalecube.services.routing.StaticAddressRouter; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; import java.time.Duration; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketLocalGatewayTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketLocalGatewayTest.java index b36c861ef..073faafd5 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketLocalGatewayTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketLocalGatewayTest.java @@ -20,8 +20,8 @@ import io.scalecube.services.gateway.ErrorService; import io.scalecube.services.gateway.ErrorServiceImpl; import io.scalecube.services.gateway.SomeException; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport; +import io.scalecube.services.routing.StaticAddressRouter; import java.time.Duration; import java.util.List; import java.util.stream.Collectors; diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketServerTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketServerTest.java index 4b30f2882..385fd3d51 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketServerTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketServerTest.java @@ -6,8 +6,8 @@ import io.scalecube.services.ServiceCall; import io.scalecube.services.annotations.Service; import io.scalecube.services.annotations.ServiceMethod; -import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport; +import io.scalecube.services.routing.StaticAddressRouter; import java.time.Duration; import java.util.stream.Collectors; import java.util.stream.IntStream; diff --git a/services-security/pom.xml b/services-security/pom.xml index a4bdcddaa..8ee60c4ae 100644 --- a/services-security/pom.xml +++ b/services-security/pom.xml @@ -5,7 +5,7 @@ io.scalecube scalecube-services-parent - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT scalecube-services-security diff --git a/services-testlib/pom.xml b/services-testlib/pom.xml index 58d7a3f12..05128f301 100644 --- a/services-testlib/pom.xml +++ b/services-testlib/pom.xml @@ -5,7 +5,7 @@ io.scalecube scalecube-services-parent - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT services-testlib diff --git a/services-transport-parent/pom.xml b/services-transport-parent/pom.xml index a8cc0bcc3..592a5c511 100644 --- a/services-transport-parent/pom.xml +++ b/services-transport-parent/pom.xml @@ -5,7 +5,7 @@ io.scalecube scalecube-services-parent - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT scalecube-services-transport-parent diff --git a/services-transport-parent/services-transport-jackson/pom.xml b/services-transport-parent/services-transport-jackson/pom.xml index 7e1bd9791..d824c9993 100644 --- a/services-transport-parent/services-transport-jackson/pom.xml +++ b/services-transport-parent/services-transport-jackson/pom.xml @@ -4,7 +4,7 @@ io.scalecube scalecube-services-transport-parent - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT scalecube-services-transport-jackson diff --git a/services-transport-parent/services-transport-rsocket/pom.xml b/services-transport-parent/services-transport-rsocket/pom.xml index 66d5d4b9f..77cd9c514 100644 --- a/services-transport-parent/services-transport-rsocket/pom.xml +++ b/services-transport-parent/services-transport-rsocket/pom.xml @@ -4,7 +4,7 @@ io.scalecube scalecube-services-transport-parent - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT scalecube-services-transport-rsocket diff --git a/services/pom.xml b/services/pom.xml index bc2e28d7f..6d659286e 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -5,7 +5,7 @@ scalecube-services-parent io.scalecube - 2.12.4-SNAPSHOT + 2.12.5-SNAPSHOT scalecube-services diff --git a/services/src/test/java/io/scalecube/services/ServiceAuthRemoteTest.java b/services/src/test/java/io/scalecube/services/ServiceAuthRemoteTest.java deleted file mode 100644 index 4819ac330..000000000 --- a/services/src/test/java/io/scalecube/services/ServiceAuthRemoteTest.java +++ /dev/null @@ -1,328 +0,0 @@ -package io.scalecube.services; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import io.rsocket.exceptions.RejectedSetupException; -import io.scalecube.services.Microservices.Context; -import io.scalecube.services.auth.Authenticator; -import io.scalecube.services.auth.PrincipalMapper; -import io.scalecube.services.discovery.ScalecubeServiceDiscovery; -import io.scalecube.services.discovery.api.ServiceDiscovery; -import io.scalecube.services.exceptions.UnauthorizedException; -import io.scalecube.services.sut.security.AnotherSecuredService; -import io.scalecube.services.sut.security.AnotherSecuredServiceImpl; -import io.scalecube.services.sut.security.PartiallySecuredService; -import io.scalecube.services.sut.security.PartiallySecuredServiceImpl; -import io.scalecube.services.sut.security.SecuredService; -import io.scalecube.services.sut.security.SecuredServiceImpl; -import io.scalecube.services.sut.security.UserProfile; -import io.scalecube.services.transport.rsocket.RSocketServiceTransport; -import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; -import java.time.Duration; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -final class ServiceAuthRemoteTest { - - private static final Duration TIMEOUT = Duration.ofSeconds(10); - - private static final Map CREDENTIALS = new HashMap<>(); - - static { - CREDENTIALS.put("username", "Alice"); - CREDENTIALS.put("password", "qwerty"); - } - - private static final Map INVALID_CREDENTIALS = new HashMap<>(); - - static { - INVALID_CREDENTIALS.put("username", "Eve"); - INVALID_CREDENTIALS.put("password", "admin123"); - } - - private static final Authenticator> authenticator = - headers -> { - String username = headers.get("username"); - String password = headers.get("password"); - - if ("Alice".equals(username) && "qwerty".equals(password)) { - HashMap authData = new HashMap<>(); - authData.put("name", "Alice"); - authData.put("role", "ADMIN"); - return Mono.just(authData); - } - - return Mono.error( - new UnauthorizedException("Authentication failed (username or password incorrect)")); - }; - - private static Microservices service; - private static Microservices serviceWithoutAuthenticator; - private static Microservices partiallySecuredService; - public static PrincipalMapper, UserProfile> principalMapper; - - @BeforeAll - static void beforeAll() { - StepVerifier.setDefaultTimeout(TIMEOUT); - - principalMapper = authData -> new UserProfile(authData.get("name"), authData.get("role")); - - service = - Microservices.start( - new Context() - .discovery( - serviceEndpoint -> - new ScalecubeServiceDiscovery() - .transport(cfg -> cfg.transportFactory(new WebsocketTransportFactory())) - .options(opts -> opts.metadata(serviceEndpoint))) - .transport(() -> new RSocketServiceTransport().authenticator(authenticator)) - .services( - ServiceInfo.fromServiceInstance(new SecuredServiceImpl()) - .principalMapper(principalMapper) - .build())); - - serviceWithoutAuthenticator = - Microservices.start( - new Context() - .discovery( - serviceEndpoint -> - new ScalecubeServiceDiscovery() - .transport(cfg -> cfg.transportFactory(new WebsocketTransportFactory())) - .options(opts -> opts.metadata(serviceEndpoint))) - .transport(RSocketServiceTransport::new) - .services( - ServiceInfo.fromServiceInstance(new AnotherSecuredServiceImpl()) - .principalMapper(principalMapper) - .build())); - - partiallySecuredService = - Microservices.start( - new Context() - .discovery( - serviceEndpoint -> - new ScalecubeServiceDiscovery() - .transport(cfg -> cfg.transportFactory(new WebsocketTransportFactory())) - .options(opts -> opts.metadata(serviceEndpoint))) - .transport(() -> new RSocketServiceTransport().authenticator(authenticator)) - .services( - ServiceInfo.fromServiceInstance(new PartiallySecuredServiceImpl()) - .principalMapper(principalMapper) - .build())); - } - - @AfterAll - static void afterAll() { - if (service != null) { - service.close(); - } - - if (serviceWithoutAuthenticator != null) { - serviceWithoutAuthenticator.close(); - } - - if (partiallySecuredService != null) { - partiallySecuredService.close(); - } - } - - @Test - @DisplayName("Successful authentication") - void successfulAuthentication() { - try (Microservices caller = newCaller()) { - SecuredService securedService = caller.call().api(SecuredService.class); - - StepVerifier.create(securedService.helloWithRequest("Bob")) - .assertNext(response -> assertEquals("Hello, Bob", response)) - .verifyComplete(); - - StepVerifier.create(securedService.helloWithPrincipal()) - .assertNext(response -> assertEquals("Hello, Alice", response)) - .verifyComplete(); - - StepVerifier.create(securedService.helloWithRequestAndPrincipal("Bob")) - .assertNext(response -> assertEquals("Hello, Bob and Alice", response)) - .verifyComplete(); - } - } - - @Test - @DisplayName("Authentication failed if authenticator not provided") - void failedAuthenticationWhenAuthenticatorNotProvided() { - try (Microservices caller = newCaller()) { - AnotherSecuredService securedService = caller.call().api(AnotherSecuredService.class); - - Consumer verifyError = - th -> { - assertEquals(UnauthorizedException.class, th.getClass()); - assertEquals("Authentication failed", th.getMessage()); - }; - - StepVerifier.create(securedService.helloWithRequest("Bob")) - .expectErrorSatisfies(verifyError) - .verify(); - - StepVerifier.create(securedService.helloWithPrincipal()) - .expectErrorSatisfies(verifyError) - .verify(); - - StepVerifier.create(securedService.helloWithRequestAndPrincipal("Bob")) - .expectErrorSatisfies(verifyError) - .verify(); - } - } - - @Test - @DisplayName("Authentication failed with empty credentials") - void failedAuthenticationWithEmptyCredentials() { - try (Microservices caller = newEmptyCredentialsCaller()) { - SecuredService securedService = caller.call().api(SecuredService.class); - - Consumer verifyError = - th -> { - assertEquals(UnauthorizedException.class, th.getClass()); - assertEquals("Authentication failed", th.getMessage()); - }; - - StepVerifier.create(securedService.helloWithRequest("Bob")) - .expectErrorSatisfies(verifyError) - .verify(); - - StepVerifier.create(securedService.helloWithPrincipal()) - .expectErrorSatisfies(verifyError) - .verify(); - - StepVerifier.create(securedService.helloWithRequestAndPrincipal("Bob")) - .expectErrorSatisfies(verifyError) - .verify(); - } - } - - @Disabled("https://github.com/scalecube/scalecube-services/issues/882") - @Test - @DisplayName("Authentication failed with invalid credentials") - void failedAuthenticationWithInvalidCredentials() { - try (Microservices caller = newInvalidCredentialsCaller()) { - SecuredService securedService = caller.call().api(SecuredService.class); - - Consumer verifyError = - th -> { - assertEquals(RejectedSetupException.class, th.getClass()); - assertEquals("Authentication failed (username or password incorrect)", th.getMessage()); - }; - - StepVerifier.create(securedService.helloWithRequest("Bob")) - .expectErrorSatisfies(verifyError) - .verify(); - - StepVerifier.create(securedService.helloWithPrincipal()) - .expectErrorSatisfies(verifyError) - .verify(); - - StepVerifier.create(securedService.helloWithRequestAndPrincipal("Bob")) - .expectErrorSatisfies(verifyError) - .verify(); - } - } - - @Test - @DisplayName("Successful authentication of partially secured service") - void successfulAuthenticationOnPartiallySecuredService() { - try (Microservices caller = newCaller()) { - StepVerifier.create(caller.call().api(PartiallySecuredService.class).securedMethod("Alice")) - .assertNext(response -> assertEquals("Hello, Alice", response)) - .verifyComplete(); - StepVerifier.create(caller.call().api(PartiallySecuredService.class).publicMethod("Alice")) - .assertNext(response -> assertEquals("Hello, Alice", response)) - .verifyComplete(); - } - } - - @Test - @DisplayName("Successful call public method of partially secured service without authentication") - void successfulCallOfPublicMethodWithoutAuthentication() { - try (Microservices caller = newCaller()) { - StepVerifier.create(caller.call().api(PartiallySecuredService.class).publicMethod("Alice")) - .assertNext(response -> assertEquals("Hello, Alice", response)) - .verifyComplete(); - - Consumer verifyError = - th -> { - assertEquals(UnauthorizedException.class, th.getClass()); - assertEquals("Authentication failed", th.getMessage()); - }; - - StepVerifier.create(caller.call().api(PartiallySecuredService.class).securedMethod("Alice")) - .verifyErrorSatisfies(verifyError); - } - } - - private static Microservices newCaller() { - return Microservices.start( - new Context() - .discovery(ServiceAuthRemoteTest::serviceDiscovery) - .transport( - () -> - new RSocketServiceTransport() - .credentialsSupplier(ServiceAuthRemoteTest::credentialsSupplier))); - } - - private static Microservices newEmptyCredentialsCaller() { - return Microservices.start( - new Context() - .discovery(ServiceAuthRemoteTest::serviceDiscovery) - .transport(RSocketServiceTransport::new)); - } - - private static Microservices newInvalidCredentialsCaller() { - return Microservices.start( - new Context() - .discovery(ServiceAuthRemoteTest::serviceDiscovery) - .transport( - () -> - new RSocketServiceTransport() - .credentialsSupplier(ServiceAuthRemoteTest::invalidCredentialsSupplier))); - } - - private static Mono> credentialsSupplier(ServiceReference serviceReference) { - switch (serviceReference.namespace()) { - case "anotherSecured": - case "secured": - return Mono.just(CREDENTIALS); - } - - switch (serviceReference.qualifier()) { - case "partiallySecured/publicMethod": - return Mono.empty(); - case "partiallySecured/securedMethod": - return Mono.just(CREDENTIALS); - } - - return Mono.just(Collections.emptyMap()); - } - - private static Mono> invalidCredentialsSupplier( - ServiceReference serviceReference) { - return Mono.just(INVALID_CREDENTIALS); - } - - private static ServiceDiscovery serviceDiscovery(ServiceEndpoint endpoint) { - return new ScalecubeServiceDiscovery() - .transport(cfg -> cfg.transportFactory(new WebsocketTransportFactory())) - .options(opts -> opts.metadata(endpoint)) - .membership( - opts -> - opts.seedMembers( - service.discoveryAddress().toString(), - serviceWithoutAuthenticator.discoveryAddress().toString(), - partiallySecuredService.discoveryAddress().toString())); - } -} diff --git a/services/src/test/java/io/scalecube/services/ServiceAuthTest.java b/services/src/test/java/io/scalecube/services/ServiceAuthTest.java new file mode 100644 index 000000000..03fd46ce4 --- /dev/null +++ b/services/src/test/java/io/scalecube/services/ServiceAuthTest.java @@ -0,0 +1,281 @@ +package io.scalecube.services; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import io.rsocket.exceptions.RejectedSetupException; +import io.scalecube.services.Microservices.Context; +import io.scalecube.services.api.Qualifier; +import io.scalecube.services.exceptions.UnauthorizedException; +import io.scalecube.services.sut.security.AnotherSecuredService; +import io.scalecube.services.sut.security.AnotherSecuredServiceImpl; +import io.scalecube.services.sut.security.PartiallySecuredService; +import io.scalecube.services.sut.security.PartiallySecuredServiceImpl; +import io.scalecube.services.sut.security.SecuredService; +import io.scalecube.services.sut.security.SecuredServiceImpl; +import io.scalecube.services.sut.security.UserProfile; +import io.scalecube.services.transport.api.DataCodec; +import io.scalecube.services.transport.api.HeadersCodec; +import io.scalecube.services.transport.api.ServiceTransport.CredentialsSupplier; +import io.scalecube.services.transport.rsocket.ConnectionSetupCodec; +import io.scalecube.services.transport.rsocket.RSocketClientTransport; +import io.scalecube.services.transport.rsocket.RSocketClientTransportFactory; +import io.scalecube.services.transport.rsocket.RSocketServiceTransport; +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Consumer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; +import reactor.netty.resources.LoopResources; +import reactor.test.StepVerifier; + +final class ServiceAuthTest { + + private static final Duration TIMEOUT = Duration.ofSeconds(10); + private static final LoopResources LOOP_RESOURCE = LoopResources.create("exberry-service-call"); + + private static Microservices service; + + @SuppressWarnings("unchecked") + @BeforeAll + static void beforeAll() { + StepVerifier.setDefaultTimeout(TIMEOUT); + + service = + Microservices.start( + new Context() + .transport( + () -> + new RSocketServiceTransport().authenticator(ServiceAuthTest::authenticate)) + .services( + ServiceInfo.fromServiceInstance(new SecuredServiceImpl()) + .principalMapper(p -> mapPrincipal((Map) p)) + .build(), + ServiceInfo.fromServiceInstance(new AnotherSecuredServiceImpl()) + .principalMapper(p -> mapPrincipal((Map) p)) + .build(), + ServiceInfo.fromServiceInstance(new PartiallySecuredServiceImpl()) + .principalMapper(p -> mapPrincipal((Map) p)) + .build())); + } + + @AfterAll + static void afterAll() { + if (service != null) { + service.close(); + } + } + + @Test + @DisplayName("Successful authentication") + void successfulAuthentication() { + try (var caller = serviceCall(ServiceAuthTest::credentials)) { + SecuredService securedService = caller.api(SecuredService.class); + + StepVerifier.create(securedService.helloWithRequest("Bob")) + .assertNext(response -> assertEquals("Hello, Bob", response)) + .verifyComplete(); + + StepVerifier.create(securedService.helloWithPrincipal()) + .assertNext(response -> assertEquals("Hello, Alice", response)) + .verifyComplete(); + + StepVerifier.create(securedService.helloWithRequestAndPrincipal("Bob")) + .assertNext(response -> assertEquals("Hello, Bob and Alice", response)) + .verifyComplete(); + } + } + + @Test + @DisplayName("Authentication failed if authenticator not provided") + void failedAuthenticationWhenAuthenticatorNotProvided() { + try (var caller = serviceCall(null)) { + AnotherSecuredService securedService = caller.api(AnotherSecuredService.class); + + Consumer verifyError = + th -> { + assertEquals(UnauthorizedException.class, th.getClass()); + assertEquals("Authentication failed", th.getMessage()); + }; + + StepVerifier.create(securedService.helloWithRequest("Bob")) + .expectErrorSatisfies(verifyError) + .verify(); + + StepVerifier.create(securedService.helloWithPrincipal()) + .expectErrorSatisfies(verifyError) + .verify(); + + StepVerifier.create(securedService.helloWithRequestAndPrincipal("Bob")) + .expectErrorSatisfies(verifyError) + .verify(); + } + } + + @Test + @DisplayName("Authentication failed with empty credentials") + void failedAuthenticationWithEmptyCredentials() { + try (var caller = serviceCall(null)) { + SecuredService securedService = caller.api(SecuredService.class); + + Consumer verifyError = + th -> { + assertEquals(UnauthorizedException.class, th.getClass()); + assertEquals("Authentication failed", th.getMessage()); + }; + + StepVerifier.create(securedService.helloWithRequest("Bob")) + .expectErrorSatisfies(verifyError) + .verify(); + + StepVerifier.create(securedService.helloWithPrincipal()) + .expectErrorSatisfies(verifyError) + .verify(); + + StepVerifier.create(securedService.helloWithRequestAndPrincipal("Bob")) + .expectErrorSatisfies(verifyError) + .verify(); + } + } + + @Test + @DisplayName("Authentication failed with invalid credentials") + void failedAuthenticationWithInvalidCredentials() { + try (var caller = serviceCall(ServiceAuthTest::invalidCredentials)) { + SecuredService securedService = caller.api(SecuredService.class); + + Consumer verifyError = + th -> { + assertEquals(RejectedSetupException.class, th.getClass()); + assertEquals("Authentication failed (username or password incorrect)", th.getMessage()); + }; + + StepVerifier.create(securedService.helloWithRequest("Bob")) + .expectErrorSatisfies(verifyError) + .verify(); + + StepVerifier.create(securedService.helloWithPrincipal()) + .expectErrorSatisfies(verifyError) + .verify(); + + StepVerifier.create(securedService.helloWithRequestAndPrincipal("Bob")) + .expectErrorSatisfies(verifyError) + .verify(); + } + } + + @Test + @DisplayName("Successful authentication of partially secured service") + void successfulAuthenticationOnPartiallySecuredService() { + try (var caller = serviceCall(ServiceAuthTest::credentials)) { + StepVerifier.create(caller.api(PartiallySecuredService.class).securedMethod("Alice")) + .assertNext(response -> assertEquals("Hello, Alice", response)) + .verifyComplete(); + + StepVerifier.create(caller.api(PartiallySecuredService.class).publicMethod("Alice")) + .assertNext(response -> assertEquals("Hello, Alice", response)) + .verifyComplete(); + } + } + + @Test + @DisplayName("Successful call public method of partially secured service without authentication") + void successfulCallOfPublicMethodWithoutAuthentication() { + try (var caller = serviceCall(ServiceAuthTest::credentials)) { + StepVerifier.create(caller.api(PartiallySecuredService.class).publicMethod("publicMethod")) + .assertNext(response -> assertEquals("Hello, publicMethod", response)) + .verifyComplete(); + + StepVerifier.create(caller.api(PartiallySecuredService.class).publicMethod("securedMethod")) + .assertNext(response -> assertEquals("Hello, securedMethod", response)) + .verifyComplete(); + } + } + + private static Mono> credentials(ServiceReference serviceReference) { + final Map credentials = new HashMap<>(); + credentials.put("username", "Alice"); + credentials.put("password", "qwerty"); + + switch (serviceReference.namespace()) { + case "anotherSecured": + case "secured": + return Mono.just(credentials); + } + + switch (serviceReference.qualifier()) { + case "partiallySecured/publicMethod": + return Mono.empty(); + case "partiallySecured/securedMethod": + return Mono.just(credentials); + } + + return Mono.just(Collections.emptyMap()); + } + + private static Mono> invalidCredentials(ServiceReference serviceReference) { + final Map credentials = new HashMap<>(); + credentials.put("username", "Invalid-User"); + credentials.put("password", "Invalid-Password"); + return Mono.just(credentials); + } + + private static Mono> authenticate(Map headers) { + String username = headers.get("username"); + String password = headers.get("password"); + + if ("Alice".equals(username) && "qwerty".equals(password)) { + HashMap authData = new HashMap<>(); + authData.put("name", "Alice"); + authData.put("role", "ADMIN"); + return Mono.just(authData); + } + + return Mono.error( + new UnauthorizedException("Authentication failed (username or password incorrect)")); + } + + private static UserProfile mapPrincipal(Map authData) { + return new UserProfile(authData.get("name"), authData.get("role")); + } + + @SuppressWarnings("resource") + private static ServiceCall serviceCall(CredentialsSupplier credentialsSupplier) { + final var loopResources = LOOP_RESOURCE; + final var address = service.serviceAddress(); + + return new ServiceCall() + .transport( + new RSocketClientTransport( + credentialsSupplier, + ConnectionSetupCodec.DEFAULT_INSTANCE, + HeadersCodec.DEFAULT_INSTANCE, + DataCodec.getAllInstances(), + RSocketClientTransportFactory.websocket().apply(loopResources))) + .router( + (serviceRegistry, request) -> { + final var qualifier = request.qualifier(); + final var ind = qualifier.lastIndexOf(Qualifier.DELIMITER); + final var ns = qualifier.substring(0, ind); + final var action = qualifier.substring(ind + 1); + + final var serviceReference = + new ServiceReference( + new ServiceMethodDefinition( + action, Collections.emptyMap(), credentialsSupplier != null, null), + new ServiceRegistration(ns, Collections.emptyMap(), Collections.emptyList()), + ServiceEndpoint.builder() + .id(UUID.randomUUID().toString()) + .address(address) + .build()); + + return Optional.of(serviceReference); + }); + } +} diff --git a/services/src/test/java/io/scalecube/services/transport/rsocket/RSocketNettyColocatedEventLoopGroupTest.java b/services/src/test/java/io/scalecube/services/transport/rsocket/RSocketColocatedEventLoopTest.java similarity index 98% rename from services/src/test/java/io/scalecube/services/transport/rsocket/RSocketNettyColocatedEventLoopGroupTest.java rename to services/src/test/java/io/scalecube/services/transport/rsocket/RSocketColocatedEventLoopTest.java index e281202d0..917a9bdf9 100644 --- a/services/src/test/java/io/scalecube/services/transport/rsocket/RSocketNettyColocatedEventLoopGroupTest.java +++ b/services/src/test/java/io/scalecube/services/transport/rsocket/RSocketColocatedEventLoopTest.java @@ -19,7 +19,7 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -public class RSocketNettyColocatedEventLoopGroupTest { +public class RSocketColocatedEventLoopTest { private Microservices ping; private Microservices pong;