Skip to content

Commit 2fa92f6

Browse files
committed
WIP on improvements for RequestContext
1 parent f45713a commit 2fa92f6

File tree

2 files changed

+49
-40
lines changed

2 files changed

+49
-40
lines changed

services-api/src/main/java/io/scalecube/services/RequestContext.java

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.scalecube.services;
22

3+
import static io.scalecube.services.MaskUtil.mask;
34
import static io.scalecube.services.api.ServiceMessage.HEADER_QUALIFIER;
45
import static io.scalecube.services.api.ServiceMessage.HEADER_REQUEST_METHOD;
56
import static io.scalecube.services.api.ServiceMessage.HEADER_UPLOAD_FILENAME;
@@ -10,36 +11,42 @@
1011
import io.scalecube.services.methods.MethodInfo;
1112
import java.math.BigDecimal;
1213
import java.math.BigInteger;
14+
import java.util.Collections;
1315
import java.util.Map;
1416
import java.util.Map.Entry;
1517
import java.util.Objects;
18+
import java.util.StringJoiner;
1619
import java.util.stream.Stream;
1720
import org.slf4j.Logger;
1821
import org.slf4j.LoggerFactory;
1922
import reactor.core.publisher.Mono;
2023
import reactor.util.context.Context;
2124

2225
/**
23-
* Represents request-specific context that holds metadata related to the current request. It acts
24-
* as a wrapper around an existing {@link Context} and provides additional methods for accessing and
25-
* managing request-specific data such as headers, principal, method information, and path
26-
* variables.
26+
* Request-scoped context that wraps Reactor {@link Context} and provides typed accessors for
27+
* request metadata (headers, principal, method info, etc.).
2728
*/
2829
public class RequestContext implements Context {
2930

3031
private static final Logger LOGGER = LoggerFactory.getLogger(RequestContext.class);
3132

33+
private static final Object HEADERS_KEY = new Object();
34+
private static final Object REQUEST_KEY = new Object();
35+
private static final Object PRINCIPAL_KEY = new Object();
36+
private static final Object METHOD_INFO_KEY = new Object();
37+
private static final Object PATH_VARS_KEY = new Object();
38+
3239
private final Context source;
3340

34-
/** Creates new {@code RequestContext} with an empty base context. */
41+
/** Creates new {@code RequestContext} with empty base context. */
3542
public RequestContext() {
3643
this(Context.empty());
3744
}
3845

3946
/**
40-
* Creates new {@code RequestContext} based on an existing context.
47+
* Creates new {@code RequestContext} based on existing context.
4148
*
42-
* @param source the source context to wrap
49+
* @param source source context to wrap
4350
*/
4451
public RequestContext(Context source) {
4552
this.source = Objects.requireNonNull(source, "source");
@@ -98,7 +105,7 @@ public Context delete(Object key) {
98105
* @return headers, or {@code null} if not set
99106
*/
100107
public Map<String, String> headers() {
101-
return source.getOrDefault("headers", null);
108+
return source.getOrDefault(HEADERS_KEY, Collections.emptyMap());
102109
}
103110

104111
/**
@@ -108,7 +115,7 @@ public Map<String, String> headers() {
108115
* @return new {@code RequestContext} instance with updated headers
109116
*/
110117
public RequestContext headers(Map<String, String> headers) {
111-
return put("headers", headers);
118+
return put(HEADERS_KEY, headers);
112119
}
113120

114121
/**
@@ -117,7 +124,7 @@ public RequestContext headers(Map<String, String> headers) {
117124
* @return request, or {@code null} if not set
118125
*/
119126
public Object request() {
120-
return source.getOrDefault("request", null);
127+
return source.getOrDefault(REQUEST_KEY, null);
121128
}
122129

123130
/**
@@ -127,7 +134,7 @@ public Object request() {
127134
* @return new {@code RequestContext} instance with updated request
128135
*/
129136
public RequestContext request(Object request) {
130-
return put("request", request);
137+
return put(REQUEST_KEY, request);
131138
}
132139

133140
/**
@@ -137,8 +144,7 @@ public RequestContext request(Object request) {
137144
* @return header value, or {@code null} if not found
138145
*/
139146
public String header(String name) {
140-
final var headers = headers();
141-
return headers != null ? headers.get(name) : null;
147+
return headers().get(name);
142148
}
143149

144150
/**
@@ -174,7 +180,7 @@ public String uploadFilename() {
174180
* @return {@link Principal} associated with the request, or {@code null} if not set
175181
*/
176182
public Principal principal() {
177-
return getOrDefault("principal", null);
183+
return getOrDefault(PRINCIPAL_KEY, null);
178184
}
179185

180186
/**
@@ -184,7 +190,7 @@ public Principal principal() {
184190
* @return new {@code RequestContext} instance with the updated principal
185191
*/
186192
public RequestContext principal(Principal principal) {
187-
return put("principal", principal);
193+
return put(PRINCIPAL_KEY, principal);
188194
}
189195

190196
/**
@@ -204,7 +210,7 @@ public boolean hasPrincipal() {
204210
* @return {@link MethodInfo} associated with the request, or {@code null} if not set
205211
*/
206212
public MethodInfo methodInfo() {
207-
return getOrDefault("methodInfo", null);
213+
return getOrDefault(METHOD_INFO_KEY, null);
208214
}
209215

210216
/**
@@ -214,7 +220,7 @@ public MethodInfo methodInfo() {
214220
* @return new {@code RequestContext} instance with the updated {@link MethodInfo}
215221
*/
216222
public RequestContext methodInfo(MethodInfo methodInfo) {
217-
return put("methodInfo", methodInfo);
223+
return put(METHOD_INFO_KEY, methodInfo);
218224
}
219225

220226
/**
@@ -223,7 +229,7 @@ public RequestContext methodInfo(MethodInfo methodInfo) {
223229
* @return path variables, or {@code null} if not set
224230
*/
225231
public Map<String, String> pathVars() {
226-
return get("pathVars");
232+
return source.getOrDefault(PATH_VARS_KEY, Collections.emptyMap());
227233
}
228234

229235
/**
@@ -232,7 +238,7 @@ public Map<String, String> pathVars() {
232238
* @return path variables, or {@code null} if not set
233239
*/
234240
public RequestContext pathVars(Map<String, String> pathVars) {
235-
return put("pathVars", pathVars);
241+
return put(PATH_VARS_KEY, pathVars);
236242
}
237243

238244
/**
@@ -242,8 +248,7 @@ public RequestContext pathVars(Map<String, String> pathVars) {
242248
* @return path variable value, or {@code null} if not found
243249
*/
244250
public String pathVar(String name) {
245-
final var pathVars = pathVars();
246-
return pathVars != null ? pathVars.get(name) : null;
251+
return pathVars().get(name);
247252
}
248253

249254
/**
@@ -281,13 +286,12 @@ public <T> T pathVar(String name, Class<T> type) {
281286
return (T) new BigInteger(s);
282287
}
283288

284-
throw new IllegalArgumentException("Wrong pathVar type: " + type);
289+
throw new IllegalArgumentException("Unsupported pathVar type: " + type);
285290
}
286291

287292
/**
288-
* Retrieves {@link RequestContext} from the reactor context in deferred manner.
289-
*
290-
* @return {@link Mono} emitting the {@code RequestContext}, or error - if it is missing
293+
* Retrieves {@link RequestContext} from the reactor context, wrapping the existing context if
294+
* necessary.
291295
*/
292296
public static Mono<RequestContext> deferContextual() {
293297
return Mono.deferContextual(context -> Mono.just(context.get(RequestContext.class)));
@@ -318,9 +322,8 @@ public static Mono<RequestContext> deferSecured() {
318322
context -> {
319323
if (!context.hasPrincipal()) {
320324
LOGGER.warn(
321-
"Insufficient permissions for secured method ({}) -- "
322-
+ "request context ({}) does not have principal",
323-
context.methodInfo(),
325+
"Insufficient permissions -- "
326+
+ "request context does not have principal, request context: {}",
324327
context);
325328
throw new ForbiddenException("Insufficient permissions");
326329
}
@@ -333,11 +336,10 @@ public static Mono<RequestContext> deferSecured() {
333336
&& !allowedRoles.isEmpty()
334337
&& !allowedRoles.contains(principal.role())) {
335338
LOGGER.warn(
336-
"Insufficient permissions for secured method ({}) -- "
337-
+ "principal role '{}' is not allowed (principal: {})",
338-
context.methodInfo(),
339+
"Insufficient permissions -- "
340+
+ "principal role '{}' is not allowed, request context: {}",
339341
principal.role(),
340-
principal);
342+
context);
341343
throw new ForbiddenException("Insufficient permissions");
342344
}
343345

@@ -346,15 +348,25 @@ public static Mono<RequestContext> deferSecured() {
346348
for (var allowedPermission : allowedPermissions) {
347349
if (!principal.hasPermission(allowedPermission)) {
348350
LOGGER.warn(
349-
"Insufficient permissions for secured method ({}) -- "
350-
+ "allowed permission '{}' is missing (principal: {})",
351-
context.methodInfo(),
351+
"Insufficient permissions -- "
352+
+ "allowed permission '{}' is missing, request context: {}",
352353
allowedPermission,
353-
principal);
354+
context);
354355
throw new ForbiddenException("Insufficient permissions");
355356
}
356357
}
357358
}
358359
});
359360
}
361+
362+
@Override
363+
public String toString() {
364+
return new StringJoiner(", ", RequestContext.class.getSimpleName() + "[", "]")
365+
.add("principal=" + principal())
366+
.add("methodInfo=" + methodInfo())
367+
.add("headers=" + mask(headers()))
368+
.add("pathVars=" + mask(pathVars()))
369+
.add("sourceKeys=" + source.stream().map(Entry::getKey).toList())
370+
.toString();
371+
}
360372
}

services-api/src/main/java/io/scalecube/services/methods/ServiceMethodInvoker.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -313,10 +313,7 @@ private Mono<Principal> mapPrincipal(RequestContext context) {
313313
return Mono.just(context.principal());
314314
} else {
315315
LOGGER.warn(
316-
"Insufficient permissions for secured method ({}) -- "
317-
+ "request context ({}) does not have principal, "
318-
+ "and principalMapper is also not set",
319-
methodInfo,
316+
"Insufficient permissions -- principal is missing or invalid, request context: {}",
320317
context);
321318
throw new ForbiddenException("Insufficient permissions");
322319
}

0 commit comments

Comments
 (0)