ZeroMQ - Get user ID from ZeroMQ Auth response - zeromq

In ZeroMQ, more precisely cppzmq in my case, when you use ZAP,
it is handled completely transparently for the user.
However, as documented in the RFC: https://rfc.zeromq.org/spec/27/,
the Auth server can send back a user ID and some metadata in the response:
The reply message SHALL consist of the following message frames:
An address delimiter frame, which SHALL have a length of zero.
The version frame, which SHALL contain the three octets “1.0”.
The request id, which MAY contain an opaque binary blob.
The status code, which SHALL contain a string.
The status text, which MAY contain a string.
The user id, which SHALL contain a string.
The metadata, which MAY contain a blob.
A little bit further down, it precises the meaning of user id:
user id: this MAY provide the user identity in case of a 200 status, for use by applications. For other statuses, it SHALL be empty.
My question is the following: how can you retrieve the user ID from the response with cppzmq?
Small code example to show what I mean:
void client() {
zmq::context_t context;
zmq::socket_t socket (context, zmq::socket_type::req);
socket.set(zmq::sockopt::zap_domain, "global");
socket.set(zmq::sockopt::plain_username, "user");
socket.set(zmq::sockopt::plain_password, "password");
socket.connect("tcp://127.0.0.1:4242");
...
std::string auth_user_id = /* magic function here */;
}

A minimal empty reference implementation is provided in the RFC repository at https://github.com/zeromq/rfc/blob/master/src/spec_27.c. This implementation demonstrates a server talking to a proxy handler over inproc://, talking to an external terminal handler over TCP. The terminal handler implements a PLAIN authentication mechanism.

Related

Start processing Flux response from server before completion: is it possible?

I have 2 Spring-Boot-Reactive apps, one server and one client; the client calls the server like so:
Flux<Thing> things = thingsApi.listThings(5);
And I want to have this as a list for later use:
// "extractContent" operation takes 1.5s per "thing"
List<String> thingsContent = things.map(ThingConverter::extractContent)
.collect(Collectors.toList())
.block()
On the server side, the endpoint definition looks like this:
#Override
public Mono<ResponseEntity<Flux<Thing>>> listThings(
#NotNull #Valid #RequestParam(value = "nbThings") Integer nbThings,
ServerWebExchange exchange
) {
// "getThings" operation takes 1.5s per "thing"
Flux<Thing> things = thingsService.getThings(nbThings);
return Mono.just(new ResponseEntity<>(things, HttpStatus.OK));
}
The signature comes from the Open-API generated code (Spring-Boot server, reactive mode).
What I observe: the client jumps to things.map immediately but only starts processing the Flux after the server has finished sending all the "things".
What I would like: the server should send the "things" as they are generated so that the client can start processing them as they arrive, effectively halving the processing time.
Is there a way to achieve this? I've found many tutorials online for the server part, but none with a java client. I've heard of server-sent events, but can my goal be achieved using a "classic" Open-API endpoint definition that returns a Flux?
The problem seemed too complex to fit a minimal viable example in the question body; full code available for reference on Github.
EDIT: redirect link to main branch after merge of the proposed solution
I've got it running by changing 2 points:
First: I've changed the content type of the response of your /things endpoint, to:
content:
text/event-stream
Don't forget to change also the default response, else the client will expect the type application/json and will wait for the whole response.
Second point: I've changed the return of ThingsService.getThings to this.getThingsFromExistingStream (the method you comment out)
I pushed my changes to a new branch fix-flux-response on your Github, so you can test them directly.

Does spring-websocket STOMP support SUBSCRIBE id?

The STOMP spec says that SUBSCRIBE MUST have id header.
https://stomp.github.io/stomp-specification-1.2.html#SUBSCRIBE_id_Header
SUBSCRIBE id Header
Since a single connection can have multiple open subscriptions with a
server, an id header MUST be included in the frame to uniquely
identify the subscription. The id header allows the client and server
to relate subsequent MESSAGE or UNSUBSCRIBE frames to the original
subscription. Within the same connection, different subscriptions MUST
use different subscription identifiers.
However, in spring's example https://spring.io/guides/gs/messaging-stomp-websocket/, it doesn't specify an id when subscribing destination.
function connect() {
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
In spring's API, the SimpMessageSendingOperations.convertAndSendToUser doesn't support an id header explicitly.
My question is how to specify id header when sending a message to client?
I don't think you can use a Subscription ID to send a message to a specific client. Stomp defines this ID and Spring's implementation uses it internally to create messages to every client subscribed to the destination address. Therefore, the Subscription ID is transparent in the Stomp communication... You can specify it in the client side or let Stomp JS (STOMP Over WebSocket) create a unique one.
If you subscribe to a destination prefixed with "/user/" and use org.springframework.messaging.simp.SimpMessagingTemplate#convertAndSendToUser or org.​springframework.​messaging.​simp.​annotation.SendToUser to send a message to a single client, what Spring does is register and create a subscription to a custom session based destination based on the original destination. In another words, from Spring's Javadoc:
When a user attempts to subscribe, e.g. to
"/user/queue/position-updates", the "/user" prefix is removed and a
unique suffix added based on the session id, e.g.
"/queue/position-updates-useri9oqdfzo" to ensure different users can
subscribe to the same logical destination without colliding.
When sending to a user, e.g.
"/user/{username}/queue/position-updates", the "/user/{username}"
prefix is removed and a suffix based on active session id's is added,
e.g. "/queue/position-updates-useri9oqdfzo".
See http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/messaging/simp/user/DefaultUserDestinationResolver.html
See https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-stomp-user-destination
EDITED:
You can't use the subscription ID to send a message direct to it's subscribed client, but you can use the client's session ID. According to here, you could use the user's name to send him a message. But you would need authenticated sessions with a Principal on it. Or you can force the destination's session ID in the header of the message, avoiding the internal step to discover it, as shown here.
private void sendMessageToUser(String destinationSessionId, String message) {
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(destinationSessionId);
headerAccessor.setLeaveMutable(true);
messagingTemplate.convertAndSendToUser(destinationSessionId, "/subscribe/private", message, headerAccessor.getMessageHeaders());
}
Doing like this, without a Principal in the session, I couldn't use #SendToUser annotation.

How does the protobuff compiler distinguish between different services and their messages with same identifiers/tags?

The question is regarding the deserialization process. I thought the first bytes describe the tag and the corresponding on-wiretyp. But what if two services using in their messages the same tag/identifier and datatype?
Protobuf actually doesn't distinguish between the two! Consider the following two messages:
message Foo {
int32 foo = 5;
}
message Bar {
int32 bar = 5;
}
Both of these messages will appear to be exactly the same. The reason is that the message doesn't carry the schema along with it. This makes the messages more compact and faster to process, with the minor downside of possibly being misinterpreted.
If you are using gRPC, the messages may appear the same, but they can be distinguished by which service they are sent to. For example:
service MyService {
rpc EatTheFoo(Foo) returns (Bar);
}
service YourService {
rpc GoToTheBar(Bar) returns (foo);
}
Even though both services take and receive messages that appear to be the same, gRPC will include the name of the service and message when sending the message. Under the hood, it will turn into an HTTP/2 request that looks like:
POST /MyService/EatTheFoo HTTP/2
which is then followed by the Foo message. If someone accidentally tried to send a Bar message, the server would see that the method name was wrong and reject the RPC. Thus, the chance of being misinterpreted is pretty small.

How does Spring WebSocket send message to a specific user?

I am reading the book Spring in Action 4 to work with STOMP messaging over WebSocket.
Suppose the user destination prefix is set as "/user" as below:
registry.setUserDestinationPrefix("/user");
Then client subscribes to a destination with below JavaScript code:
stomp.subscribe("/user/queue/notifications", handleNotifications);
Then on the server, the actual destination that the client subscribes to should be derived from its session, maybe like this:
/queue/notifications-user6hr83v6t --- (1)
Then I use the SimpMessagingTemplate to send message to that user:
messaging.convertAndSendToUser( username, "/queue/notifications",
new Notification("You just got mentioned!"));
Then the message will be sent to destination like this:
/user/<username>/queue/notifications ---(2)
Well, the two destinations (1) and (2) look different, how could the message ever reach the client?
The path
/user/<username>/queue/notifications
seems to be the "logical" path which is used in documentation. It is also initially created with convertAndSendToUser method. It is then translated into a technical format which is done in UserDestinationMessageHandler class in this line
UserDestinationResult result = this.destinationResolver.resolveDestination(message);
eg.
Given the subscription:
stompClient.subscribe('/user/queue/reply', function (greeting) { ...
sending a message with
stompClient.send("/app/personal", ...
and intercepting it with
#MessageMapping("/personal")
public void personalMessage(SimpMessageHeaderAccessor headerAccessor, PoCRequestMessage message) {
SimpMessageHeaderAccessor ha = SimpMessageHeaderAccessor
.create(SimpMessageType.MESSAGE);
ha.setSessionId(headerAccessor.getSessionId());
ha.setLeaveMutable(true);
PoCReplyMessage reply = new PoCReplyMessage("Personal Message" + message.getName());
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getSessionId(), "/queue/reply", reply, ha.getMessageHeaders());
}
the destination will be resolved as follows:
source destination: /user/zojdn53y/queue/reply
target destination: /queue/reply-userzojdn53y
this is how the final destination name is resolved.
The target destination is the real name of the queue that is created (at least as long an external message broker is used - didn't check this for a simple in-memory broker but I assume this would be the same).
One important thing to note is that when you want to use an unauthenticated user (most often scenario when experimenting with Websockets) you need to additionally put the message headers in convertAndSendToUser method - this is well described in
Spring WebSocket #SendToSession: send message to specific session

How to reply to unauthenticated user in Spring 4 STOMP over WebSocket configuration?

I'm experimenting with Spring 4 WebSocket STOMP application. Is there a way to reply to a single unauthenticated user on condition that each user has unique session ID? Right now I can only either broadcast a message or send it directly to an authenticated user.
#Controller
public class ProductController {
#MessageMapping("/products/{id}")
#SendTo("/topic") // This line makes return value to be broadcasted to every connected user.
public String getProduct(#DestinationVariable int id) {
return "Product " + id;
}
}
You can assign an anonymous identity to incoming users. There are two ways to do it.
One, you can configure a sub-class of DefaultHandshakeHandler that overrides determineUser and assigns some kind of identity to every WebSocketSession. This requires 4.0.1 by the way (currently build snapshots are available) that will be released on Monday Jan 23, 2014.
Two, the WebSocket session will fall back on the value returned from HttpServletRequest.getUserPrincipal on the handshake HTTP request. You could have a servlet Filter wrap the HttpServletRequest and decide what to return from that method. Or if you're using Spring Security which has the AnonymousAuthenticationFilter, override its createAuthentication method.
#SendToUser("/products") should result in a message to destination "/user/{username}/products". That message will be handled by the UserDestinationMessageHandler, which transforms the destination to "/products-user{sessionId}" and re-sends the message.
So I'm not quite sure what "/user/products-user0" is. It surprises me in two ways. First if it starts with "/user" then that's the destination before the transformation and should be followed by the user name (i.e. "/user/{username}/products").
The fact that it ends with "-user0" makes it look like the destination after the transformation but then it shouldn't start with "/user". In any case the 0, 1 in that case would be the WebSocket session id. What server is this?

Resources