Skip to content

Commit eb7824e

Browse files
committed
Added ITs for HttpGatewayAuthenticatorTest
1 parent a5e4cb5 commit eb7824e

File tree

2 files changed

+191
-6
lines changed

2 files changed

+191
-6
lines changed

services-gateway/src/main/java/io/scalecube/services/gateway/http/HttpGatewayAcceptor.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import io.scalecube.services.routing.StaticAddressRouter;
3131
import io.scalecube.services.transport.api.DataCodec;
3232
import java.io.IOException;
33+
import java.util.Collections;
3334
import java.util.List;
3435
import java.util.Map;
3536
import java.util.function.BiFunction;
@@ -81,10 +82,18 @@ public Publisher<Void> apply(HttpServerRequest httpRequest, HttpServerResponse h
8182
return methodNotAllowed(httpResponse);
8283
}
8384

84-
return authenticator
85-
.authenticate(httpRequest)
85+
return Mono.defer(() -> authenticator.authenticate(httpRequest))
86+
.materialize()
8687
.flatMap(
87-
principal -> {
88+
signal -> {
89+
if (signal.hasError()) {
90+
return error(
91+
httpResponse, errorMapper.toMessage(ERROR_NAMESPACE, signal.getThrowable()));
92+
}
93+
94+
final Map<String, String> principal =
95+
signal.get() != null ? signal.get() : Collections.emptyMap();
96+
8897
if (httpRequest.isMultipart()) {
8998
return handleFileUploadRequest(principal, httpRequest, httpResponse);
9099
} else {
@@ -103,7 +112,7 @@ private Mono<Void> handleFileUploadRequest(
103112
httpData ->
104113
serviceCall
105114
.requestBidirectional(
106-
createFlux(httpData)
115+
createFileFlux(httpData)
107116
.map(
108117
data ->
109118
toMessage(
@@ -150,7 +159,7 @@ private Mono<Void> handleServiceRequest(
150159
final var message =
151160
toMessage(httpRequest, builder -> builder.headers(principal).data(data));
152161

153-
// Match and handle file request
162+
// Match and handle file-download request
154163

155164
final var service = matchFileDownloadRequest(serviceRegistry.lookupService(message));
156165
if (service != null) {
@@ -346,7 +355,7 @@ private static String errorMessage(int statusCode, String fileName) {
346355
}
347356
}
348357

349-
private static Flux<byte[]> createFlux(HttpData httpData) {
358+
private static Flux<byte[]> createFileFlux(HttpData httpData) {
350359
try {
351360
return FileChannelFlux.createFrom(httpData.getFile().toPath());
352361
} catch (IOException e) {
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package io.scalecube.services.gateway.http;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.mockito.ArgumentMatchers.any;
5+
import static org.mockito.Mockito.mock;
6+
import static org.mockito.Mockito.when;
7+
8+
import io.scalecube.services.Microservices;
9+
import io.scalecube.services.Microservices.Context;
10+
import io.scalecube.services.RequestContext;
11+
import io.scalecube.services.ServiceCall;
12+
import io.scalecube.services.annotations.Service;
13+
import io.scalecube.services.annotations.ServiceMethod;
14+
import io.scalecube.services.discovery.ScalecubeServiceDiscovery;
15+
import io.scalecube.services.exceptions.UnauthorizedException;
16+
import io.scalecube.services.gateway.client.http.HttpGatewayClientTransport;
17+
import io.scalecube.services.routing.StaticAddressRouter;
18+
import io.scalecube.services.transport.rsocket.RSocketServiceTransport;
19+
import io.scalecube.transport.netty.websocket.WebsocketTransportFactory;
20+
import java.time.Duration;
21+
import java.util.Map;
22+
import java.util.StringJoiner;
23+
import org.junit.jupiter.api.AfterEach;
24+
import org.junit.jupiter.api.BeforeEach;
25+
import org.junit.jupiter.api.Test;
26+
import reactor.core.publisher.Mono;
27+
import reactor.test.StepVerifier;
28+
29+
class HttpGatewayAuthenticatorTest {
30+
31+
private static final Duration TIMEOUT = Duration.ofSeconds(3);
32+
private static final String USERNAME_PRINCIPAL_HEADER = "USERNAME_PRINCIPAL_HEADER";
33+
private static final String PASSWORD_PRINCIPAL_HEADER = "PASSWORD_PRINCIPAL_HEADER";
34+
35+
private Microservices gateway;
36+
private Microservices microservices;
37+
private final HttpGatewayAuthenticator authenticator = mock(HttpGatewayAuthenticator.class);
38+
private ServiceCall serviceCall;
39+
private ServiceBehindGateway serviceBehindGateway;
40+
41+
@BeforeEach
42+
void beforeEach() {
43+
gateway =
44+
Microservices.start(
45+
new Context()
46+
.discovery(
47+
serviceEndpoint ->
48+
new ScalecubeServiceDiscovery()
49+
.transport(cfg -> cfg.transportFactory(new WebsocketTransportFactory()))
50+
.options(opts -> opts.metadata(serviceEndpoint)))
51+
.transport(RSocketServiceTransport::new)
52+
.gateway(
53+
() -> HttpGateway.builder().id("HTTP").authenticator(authenticator).build()));
54+
55+
microservices =
56+
Microservices.start(
57+
new Context()
58+
.discovery(
59+
serviceEndpoint ->
60+
new ScalecubeServiceDiscovery()
61+
.transport(cfg -> cfg.transportFactory(new WebsocketTransportFactory()))
62+
.options(opts -> opts.metadata(serviceEndpoint))
63+
.membership(
64+
opts -> opts.seedMembers(gateway.discoveryAddress().toString())))
65+
.transport(RSocketServiceTransport::new)
66+
.defaultLogger("microservices")
67+
.services(new ServiceBehindGatewayImpl()));
68+
69+
final var gatewayAddress = gateway.gateway("HTTP").address();
70+
final var router = StaticAddressRouter.forService(gatewayAddress, "app-service").build();
71+
72+
serviceCall =
73+
new ServiceCall()
74+
.router(router)
75+
.transport(HttpGatewayClientTransport.builder().address(gatewayAddress).build());
76+
serviceBehindGateway = serviceCall.api(ServiceBehindGateway.class);
77+
}
78+
79+
@AfterEach
80+
void afterEach() {
81+
if (serviceCall != null) {
82+
serviceCall.close();
83+
}
84+
if (gateway != null) {
85+
gateway.close();
86+
}
87+
if (microservices != null) {
88+
microservices.close();
89+
}
90+
}
91+
92+
@Test
93+
void shouldAuthenticateSuccessfully() {
94+
final var username = "admin";
95+
final var password = "admin1234";
96+
when(authenticator.authenticate(any()))
97+
.thenReturn(
98+
Mono.just(
99+
Map.of(USERNAME_PRINCIPAL_HEADER, username, PASSWORD_PRINCIPAL_HEADER, password)));
100+
101+
StepVerifier.create(serviceBehindGateway.hello("hello"))
102+
.assertNext(
103+
response -> {
104+
assertEquals(username, response.username(), "username");
105+
assertEquals(password, response.password(), "password");
106+
})
107+
.expectComplete()
108+
.verify(TIMEOUT);
109+
}
110+
111+
@Test
112+
void testAuthenticationFailed() {
113+
when(authenticator.authenticate(any()))
114+
.thenReturn(Mono.error(new UnauthorizedException("Authentication failed")));
115+
116+
StepVerifier.create(serviceBehindGateway.hello("hello"))
117+
.consumeErrorWith(
118+
ex -> {
119+
final var exception = (UnauthorizedException) ex;
120+
assertEquals(401, exception.errorCode(), "errorCode");
121+
assertEquals("Authentication failed", exception.getMessage(), "errorMessage");
122+
})
123+
.verify(TIMEOUT);
124+
}
125+
126+
@Service("serviceBehindGateway")
127+
public interface ServiceBehindGateway {
128+
129+
@ServiceMethod
130+
Mono<HelloBehindGatewayResponse> hello(String name);
131+
}
132+
133+
public static class ServiceBehindGatewayImpl implements ServiceBehindGateway {
134+
135+
@Override
136+
public Mono<HelloBehindGatewayResponse> hello(String name) {
137+
return RequestContext.deferContextual()
138+
.map(
139+
context -> {
140+
final var headers = context.headers();
141+
final var username = headers.get(USERNAME_PRINCIPAL_HEADER);
142+
final var password = headers.get(PASSWORD_PRINCIPAL_HEADER);
143+
return new HelloBehindGatewayResponse(username, password);
144+
});
145+
}
146+
}
147+
148+
public static class HelloBehindGatewayResponse {
149+
150+
private String username;
151+
private String password;
152+
153+
public HelloBehindGatewayResponse() {}
154+
155+
public HelloBehindGatewayResponse(String username, String password) {
156+
this.username = username;
157+
this.password = password;
158+
}
159+
160+
public String username() {
161+
return username;
162+
}
163+
164+
public String password() {
165+
return password;
166+
}
167+
168+
@Override
169+
public String toString() {
170+
return new StringJoiner(", ", HelloBehindGatewayResponse.class.getSimpleName() + "[", "]")
171+
.add("username='" + username + "'")
172+
.add("password='" + password + "'")
173+
.toString();
174+
}
175+
}
176+
}

0 commit comments

Comments
 (0)