I have a project where I'm using Moya with RxSwift extensions.
The simple use cases work fine, I'm able to do requests and get responses in the form of Observables.
public func test() -> Observable<Response> {
return provider
.request(.test)
.retry(5)
}
I can then subscribe to the observable and print the response with no problem.
But now I need to handle authentication logic. The way it works is that I run the above request with a token added as an HTTP Header Field. Moya allows me to that transparently by using endpointByAddingHTTPHeaderFields in the endpointClosure. No problem so far.
The problem arises when the request fails with HTTP status 401, that means I need to re-authenticate by calling another endpoint
provider.request(.auth(user, pass)).retry(5)
This returns another Observable that I can easily map to JSON to get the new token.
I then just have to call test() again!
So my question is... How can I add this authentication logic inside the test() function above, so that the Observable returned by test() is already guaranteed to have run the re-authentication logic in case of failure and be the result of a second re-authenticated request.
I'm very new to RXSwift and RX in general, so I'm a bit clueless about the operators I would use to do this.
Thanks!
public func test(with authToken: String) -> Observable<Response> {
return provider
.request(.test)
.endpointByAddingHTTPHeaderFields(["Authorization": authToken])
.catchError { error in
if needsReauth(error) {
return provider.request(.auth(user, pass)).map { parseToken($0) }
.flatMap { token in
return test(with: token)
}
} else {
return .error(error)
}
}
}
catchError enables to continue observable's execution using another observable. The observable we define here does the following:
First, it will request the .auth endpoint.
It then reads from the response to get the new auth token
Finally, we recursively call test(with authToken: String) to retry querying the test enpoint.
Related
Trying to validate some JSON values outside of the first method. What would be the best way to do that in the second method ?
#When("^User sends API request with (.*) and no qualifiers and an State Code entry level T1.3$")
public void user_sends_API_request_with_no_qualifiers_and_an_entry_level_T(String path) {
response =
given()
.auth().oauth2(DomaniRx_RestAssuredOAuth2.access_token_code)
.queryParam("PhamacyID","12345")
.queryParam("Customer Set Type ID", "CustomerSetTypeValue")
.queryParam("PharmacyListIDShortName","Value")
.queryParam("DateFilled","04/17/2022")
.get(defaultURL+path)
.then()
.statusCode(200)
.extract().response();
}
#Then("^The Pharmacy ID in the response should match the (\\d+) in the request T1.3$")
public void the_Pharmacy_ID_in_the_response_should_match_the_in_the_request_T(String PharmacyID) {
}
I don't know the best way, but I think one way to achieve that is put response is an instance variable, then you can access it from other method in same class.
If you want to share response outside class, then use class (static) variable
You can assign your response body to a variable like the below:
Then you can make assertions easily on the other methods. You can define a ResponseModel according to your response. Its name can be changed. it's up to you.
private ResponseModel response;
.
response =
given()
.auth().oauth2(DomaniRx_RestAssuredOAuth2.access_token_code)
.queryParam("PhamacyID","12345")
.queryParam("Customer Set Type ID", "CustomerSetTypeValue")
.queryParam("PharmacyListIDShortName","Value")
.queryParam("DateFilled","04/17/2022")
.get(defaultURL+path)
.then()
.statusCode(200)
.extract().response()
.jsonPath().getObject(".", ResponseModel .class);
Below code return Mono from ReactiveSecurityContextHolder
#Component
public class SomeClass {
public static Mono<String> issuesID() {
return ReactiveSecurityContextHolder.getContext().switchIfEmpty(Mono.empty()).flatMap((securityContext) -> {
Authentication authentication = securityContext.getAuthentication();
Jwt jwt = (Jwt) authentication.getPrincipal();
String issuer = (String) jwt.getClaims().get("some_claim");
log.info("{}",issuer);
return Mono.justOrEmpty(issuer);
}).switchIfEmpty(Mono.empty());
}
}
I need something like this
String mutatedId = PREXIX+SomeClass.issuesID();
If i do followings,
PREXIX+SomeClass.issuesID().block();
PREXIX+SomeClass.issuesID().subscribeOn(Schedulers.elastic()).block();
PREXIX+SomeClass.issuesID().toProcessor().block();
PREXIX+SomeClass.issuesID().toFuture().get();
They all give the same error.
block()/blockFirst()/blockLast() are blocking, which is not supported in thread rector-xxx
I have also tried delaying the Mono but that's also not helping.
I know reactor is a non-blocking framework and blocking is discourages but in my case I can't figure out another way to solve this.
I need to pass the mutatedId to mongoFactory so i can switch databases based on Jwt property per request. I do not want to inject #AuthenticationPrincipal in controller and keep passing it to downward layers and finally make decision at DOA layer.
#Override
public Mono<MongoDatabase> getMongoDatabase(String dbName) throws DataAccessException {
String mutatedId = PREXIX+SomeClass.issuesID();
return super.getMongoDatabase(mutatedId+"#"+dbName);
}
Any suggestion how this can be achieved or is there any better approach.
You use for instance flatMap.
Mono<MongoDatabase> db = issuesID()
.flatMap(id -> getMongoDatabase(id));
In Undertow I have two handlers, that are chained:
The first handler reads the request and then calls calls the second handler via next.handleRequest(exchange);
The second handler is a proxy handler which send the request to and external server where it is processed.
My problem is the first handler which reads the request. The request headers are no big deal but getting the body data of POST requests is a problem.
Existing solutions as shown in the question How to properly read POST request body in a Handler? consume the request body su that the handler chaining does not work anymore.
How can I read the request body data without consuming it or altering the request in a way that the handler chain does not work afterwards?
I found the problem, in the end it was a missing call to ByteBuffer.flip().
If someone ever needs such an POST data reader one can use the following simplified implementation of an AbstractStreamSourceConduit that is able to read the incoming POST data without consuming it:
exchange.addRequestWrapper(new ConduitWrapper<StreamSourceConduit>() {
#Override
public StreamSourceConduit wrap(ConduitFactory<StreamSourceConduit> factory, HttpServerExchange exchange) {
StreamSourceConduit source = factory.create();
return new AbstractStreamSourceConduit<StreamSourceConduit>(source) {
ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
#Override
public int read(ByteBuffer dst) throws IOException {
int x = super.read(dst);
if (x >= 0) {
ByteBuffer dup = dst.duplicate();
dup.flip();
byte[] data = new byte[x];
dup.get(data);
bout.write(data);
} else {
// end of stream reached
byte[] data = bout.toByteArray();
// ... to something with data
}
return x;
}
};
}
});
I have set up authentication in a Spring WebFlux application. The authentication mechanism appears to work fine. For example the following code is used to set up security web filter chain:
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http.authorizeExchange()
.pathMatchers("/path/to/resource").hasAuthority("A_ROLE")
.anyExchange().authenticated()
.and().httpBasic()
.and().build();
}
This works as expected in conjunction with the UserDetailsRepositoryReactiveAuthenticationManager and MapReactiveUserDetailsService. If a user doesn't have the required authority a forbidden error code is returned and otherwise the request is passed on to the handler.
I have a requirement to apply fine grained permission checks within the handler itself and figured that I should be able to retrieve the authorities from the request as follows:
public Mono<ServerResponse> getMyResource(ServerRequest serverRequest) {
Authentication authentication = (Authentication)serverRequest.principal().block();
...
}
However, I find that the principal is always null. First, is this the correct way to get a handle on the authorities, and if so is there possibly some upstream configuration I'm missing?
You are blocking the result before is available. You can simply flatmap it so that you don't have to block it.
public Mono<ServerResponse> getMyResource(ServerRequest serverRequest) {
return serverRequest.principal().flatMap((principal) -> ServerResponse.ok()
.body(fromObject("Hello " + principal.getName())));
}
UPDATE: If you want to retrieve the principal and body you could zip them.
public Mono<ServerResponse> getMyResource(ServerRequest serverRequest) {
return Mono.zip(
serverRequest.principal(),
serverRequest.bodyToMono(String.class)
).flatMap(tuple -> {
Principal principal = tuple.getT1();
String body = tuple.getT2();
return ServerResponse.ok().build();
});
}
I'm learning Dart and I've hit a roadblock. I very much want to return a value from a json string processing function so I can use that value inside main(). (I'm trying to set some top-level variables to use with a one-way data bind with an html template.) I'm using HttpRequest.getString and a .then call to kick off the processing. But HttpRequest doesn't like being assigned to a variable so I'm not sure how to get anything back out of it.
processString(String jsonString) {
// Create a map of relevant data
return myMap;
}
void main() {
HttpRequest.getString(url).then(processString);
// Do something with the processed result!
}
I guess my question is how do I get a value back from a function that was called from an HttpRequest?
You're trying to do something that the Dart async model doesn't support. You'll have to handle the result of the async request:
In processString(),
In another function called from processString(),
In an anonymous function passed to then().
Or something similar. What you can't do is access it from further down in main():
processString(String jsonString) {
// Create a map of relevant data
// Do something with the processed result!
}
void main() {
HttpRequest.getString(url).then(processString);
// Any code here can never access the result of the HttpRequest
}
You may prefer:
processString(String jsonString) {
// Create a map of relevant data
return myMap;
}
void main() {
HttpRequest.getString(url).then((resp) {
map = processString(resp);
// Do something with the processed result!
});
// Any code here can never access the result of the HttpRequest
}