Is it possible to add a custom `ChannelInboundHandler` in Micronaut? - ssl

I would like to add a custom ChannelInboundHandler in my Micronaut service so that I can listen for the SslHandshakeCompletionEvent produced after a TLS handshake has been attempted.
I seem to be able to add a ChannelOutboundHandler simply enough by annotating it with #Singleton, however when I try to do the same with a ChannelInboundHandler, it does not seem to be added to the pipeline.
What's the correct way to do this?
Edit
This looks promising: https://docs.micronaut.io/snapshot/guide/index.html#nettyPipeline

You can create an implementation of BeanCreatedEventListener<ChannelPipelineCustomizer>, and provide an implementation of the onCreated method, e.g.
#Override
public ChannelPipelineCustomizer onCreated(BeanCreatedEvent<ChannelPipelineCustomizer> event) {
ChannelPipelineCustomizer customizer = event.getBean();
if (!customizer.isServerChannel()) {
customizer.doOnConnect(pipeline -> {
pipeline.addAfter(
ChannelPipelineCustomizer.HANDLER_HTTP_CLIENT_CODEC,
"my-handler",
new MyChannelInboundHandler()
);
return pipeline;
});
}
return customizer;
}
Then, in your MyChannelInboundHandler class, implement the userEventTriggered method and listen to the SslHandshakeCompletionEvent.SUCCESS event. You can then make some assertions on e.g. the public key of some of the certificates in the chain if you're doing HPKP.

Related

Chaining Reactive Asynchronus calls in spring

I’m very new to the SpringReactor project.
Until now I've only used Mono from WebClient .bodyToMono() steps, and mostly block() those Mono's or .zip() multiple of them.
But this time I have a usecase where I need to asynchronously call methods in multiple service classes, and those multiple service classes are calling multiple backend api.
I understand Project Reactor doesn't provide asynchronous flow by default.
But we can make the publishing and/or subscribing on different thread and make code asynchronous
And that's what I am trying to do.
I tried to read the documentation here reactor reference but still not clear.
For the purpose of this question, I’m making up this imaginary scenario. that is a little closer to my use case.
Let's assume we need to get a search response from google for some texts searched under images.
Example Scenario
Let's have an endpoint in a Controller
This endpoint accepts the following object from request body
MultimediaSearchRequest{
Set<String> searchTexts; //many texts.
boolean isAddContent;
boolean isAddMetadata;
}
in the controller, I’ll break the above single request object into multiple objects of the below type.
MultimediaSingleSearchRequest{
String searchText;
boolean isAddContent;
boolean isAddMetadata;
}
This Controller talks to 3 Service classes.
Each of the service classes has a method searchSingleItem.
Each service class uses a few different backend Apis, but finally combines the results of those APIs responses into the same type of response class, let's call it MultimediaSearchResult.
class JpegSearchHandleService {
public MultimediaSearchResult searchSingleItem
(MultimediaSingleSearchRequest req){
return comboneAllImageData(
getNameApi(req),
getImageUrlApi(req),
getContentApi(req) //dont call if req.isAddContent false
)
}
}
class GifSearchHandleService {
public MultimediaSearchResult searchSingleItem
(MultimediaSingleSearchRequest req){
return comboneAllImageData(
getNameApi(req),
gitPartApi(req),
someRandomApi(req),
soemOtherRandomApi(req)
)
}
}
class VideoSearchHandleService {
public MultimediaSearchResult searchSingleItem
(MultimediaSingleSearchRequest req){
return comboneAllImageData(
getNameApi(req),
codecApi(req),
commentsApi(req),
anotherApi(req)
)
}
}
In the end, my controller returns the response as a List of MultimediaSearchResult
Class MultimediaSearchResponse{
List< MultimediaSearchResult> results;
}
If I want to use this all asynchronously using the project reactor. how to achieve it.
Like calling searchSingleItem method in each service for each searchText asynchronously.
Even within the services call each backend API asynchronously (I’m already using WebClient and converting response bodyToMono for backend API calls)
First, I will outline a solution for the upper "layer" of your scenario.
The code (a simple simulation of the scenario):
public class ChainingAsyncCallsInSpring {
public Mono<MultimediaSearchResponse> controllerEndpoint(MultimediaSearchRequest req) {
return Flux.fromIterable(req.getSearchTexts())
.map(searchText -> new MultimediaSingleSearchRequest(searchText, req.isAddContent(), req.isAddMetadata()))
.flatMap(multimediaSingleSearchRequest -> Flux.merge(
classOneSearchSingleItem(multimediaSingleSearchRequest),
classTwoSearchSingleItem(multimediaSingleSearchRequest),
classThreeSearchSingleItem(multimediaSingleSearchRequest)
))
.collectList()
.map(MultimediaSearchResponse::new);
}
private Mono<MultimediaSearchResult> classOneSearchSingleItem(MultimediaSingleSearchRequest req) {
return Mono.just(new MultimediaSearchResult("1"));
}
private Mono<MultimediaSearchResult> classTwoSearchSingleItem(MultimediaSingleSearchRequest req) {
return Mono.just(new MultimediaSearchResult("2"));
}
private Mono<MultimediaSearchResult> classThreeSearchSingleItem(MultimediaSingleSearchRequest req) {
return Mono.just(new MultimediaSearchResult("3"));
}
}
Now, some rationale.
In the controllerEndpoint() function, first we create a Flux that will emit every single searchText from the request. We map these to MultimediaSingleSearchRequest objects, so that the services can consume them with the additional metadata that was provided with the original request.
Then, Flux::flatMap the created MultimediaSingleSearchRequest objects into a merged Flux, which (as opposed to Flux::concat) ensures that all three publishers are subscribed to eagerly i.e. they don't wait for one another. It works best on this exact scenario, when several independent publishers need to be subscribed to at the same time and their order is not important.
After the flat map, at this point, we have a Flux<MultimediaSearchResult>.
We continue with Flux::collectList, thus collecting the emitted values from all publishers (we could also use Flux::reduceWith here).
As a result, we now have a Mono<List<MultimediaSearchResult>>, which can easily be mapped to a Mono<MultimediaSearchResponse>.
The results list of the MultimediaSearchResponse will have 3 items for each searchText in the original request.
Hope this was helpful!
Edit
Extending the answer with a point of view from the service classes as well. Assuming that each inner (optionally skipped) call returns a different type of result, this would be one way of going about it:
public class MultimediaSearchResult {
private Details details;
private ContentDetails content;
private MetadataDetails metadata;
}
public Mono<MultimediaSearchResult> classOneSearchSingleItem(MultimediaSingleSearchRequest req) {
return Mono.zip(getSomeDetails(req), getContentDetails(req), getMetadataDetails(req))
.map(tuple3 -> new MultimediaSearchResult(
tuple3.getT1(),
tuple3.getT2().orElse(null),
tuple3.getT3().orElse(null)
)
);
}
// Always wanted
private Mono<Details> getSomeDetails(MultimediaSingleSearchRequest req) {
return Mono.just(new Details("details")); // api call etc.
}
// Wanted if isAddContent is true
private Mono<Optional<ContentDetails>> getContentDetails(MultimediaSingleSearchRequest req) {
return req.isAddContent()
? Mono.just(Optional.of(new ContentDetails("content-details"))) // api call etc.
: Mono.just(Optional.empty());
}
// Wanted if isAddMetadata is true
private Mono<Optional<MetadataDetails>> getMetadataDetails(MultimediaSingleSearchRequest req) {
return req.isAddMetadata()
? Mono.just(Optional.of(new MetadataDetails("metadata-details"))) // api call etc.
: Mono.just(Optional.empty());
}
Optionals are used for the requests that might be skipped, since Mono::zip will fail if either of the zipped publishers emit an empty value.
If the results of each inner call extend the same base class or are the same wrapped return type, then the original answer applies as to how they can be combined (Flux::merge etc.)

InputFormatter for Single Request

Is there a way to include an InputFormatter which only runs for a single endpoint?
We have 1 solitary endpoint which has a need for a custom InputFormatter.
So we don't really want to add an input formatter globally, for the benefit of a single endpoint. I don't really want to write a hacky middleware which would run for every request either. Some kind of ActionFilter would have been perfect.
I've seen existing SO answers on this very topic, but they all have answers which require an outdated API e.g. the InputFormatters collection is no longer available on the context in Action Filters.
Cheers
Here is an example which helps you to control the input formatter for an action method.
public class CSPContentTypeFormatterAttribute : ResultFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
var options = context
.HttpContext
.RequestServices
.GetService(serviceType: typeof(IOptions<MvcOptions>)) as IOptions<MvcOptions>;
var mvcOptions = options.Value;
mvcOptions.InputFormatters.OfType<SystemTextJsonInputFormatter>().First()
.SupportedMediaTypes.Add(
new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/csp-report")
);
base.OnResultExecuting(context);
}
}

Where to verify authorization for a Command?

The question's title resumes pretty much: where do I verify authorization for a Command?
For example, setting a customer as preferred involves:
MarkAsPreferred controller action (could be Winforms or whatever);
SetCustomerAsPreferredCommand;
SetCustomerAsPreferredCommandHandler;
Customer.MarkAsPreferred() (domain);
I identified 3 places to check for authorization:
UI for displaying purposes (user should not see a link/button if he/she does not have access to it);
controller action to verify the user is authorized to call that command; commands are assumed to always succeed (regarding validation, but I'm assuming authorization too) and we have a chance to inform the user about lack of access;
inside the command just before calling domain logic;
SomeView.cshtml
if (authorizationService.Authorize("MarkCustomerAsPreferred))
{
// show link
}
CustomerController
[HttpPost]
public ActionResult MarkAsPreferred(Guid id)
{
if (!authorizationService.Authorize("MarkCustomerAsPreferred))
{
return RedirectToAction("Unauthorized");
}
var MarkCustomerAsPreferredCommand { Id = id };
...
}
MarkCustomerAsPreferredCommandHandler
public void Handle(MarkCustomerAsPreferredCommand command)
{
if (!authorizationService.Authorize("MarkCustomerAsPreferred"))
{
throw new Exception("...");
}
customer.MarkAsPreferred();
}
My question is: Do I need to verify authorization in 3 places or I'm just being overzealous?
I searched all over the internet but couldn't find any example or reference about this.
Edit
After more research and some tests I think wrapping the commands to add behavior (authorization, validation, logging) as Dennis Taub suggested is easier and cleaner to implement.
I found this blog post which explains exactly this concept.
About having multiple handlers for one command, I don't need to implement one command handler for each behavior for each original command, one wrapping command can wrap all handlers.
I think final authorization should be done on the application service level, i.e. as part of handling the command. You could wrap the command handler with an authorization handler for example.
class AuthorizationHandler : IHandle<SetCustomerAsPreferred> {
IHandle<SetCustomerAsPreferred> innerHandler;
public AuthorizationHandler(IHandle<SetCustomerAsPreferred> handler)
{
innerHandler = handler;
}
public void Handle(SetCustomerAsPreferred command)
{
if (/* not authorized */)
throw ...
innerHandler.Handle(command);
}
}
class SetCustomerAsPreferredCommandHandler : IHandle<SetCustomerAsPreferred> {
public void Handle(SetCustomerAsPreferred command)
{
// do the work
}
}
It's good UI to have that verification in the View, so the user won't click it by mistake. I consider the controller verification the 'real' one, because there is where the command is created. If an user doesn;t have the rights, she shouldn't be able to create (or even reach that action) the command.
I think that putting the check in the handler is a bit overzelous, as it's not its responsibility to do authorization and is not like that handler can be reached by an user directly.

How do I get Xtext's model from a different plugin?

I've written an Xtext-based plugin for some language. I'm now interested in creating a new independent view (as a separate plugin, though it requires my first plugin), which will interact with the currently-active DSL document - and specifically, interact with the model Xtext parsed (I think it's called the Ecore model?). How do I approach this?
I saw I can get an instance of XtextEditor if I do something like this when initializing my view:
getSite().getPage().addPartListener(new MyListener());
And then, in MyListener, override partActivated and partInputChanged to get an IWorkbenchPartReference, which is a reference to the XtextEditor. But what do I do from here? Is this even the right approach to this problem? Should I instead use some notification functionality from the Xtext side?
Found it out! First, you need an actual document:
IXtextDocument doc = editor.getDocument();
Then, if you want to access the model:
doc.modify(new IUnitOfWork.Void<XtextResource>() { // Can also use just IUnitOfWork
#Override public void process(XtextResource state) throws Exception {
...
}
});
And if you want to get live updates whenever it changes:
doc.addModelListener(new IXtextModelListener() {
#Override public void modelChanged(XtextResource resource) {
for (EObject model : resource.getContent()) {
...
}
}
});

Set ActiveMQ message header while using Camel's #Produce annotation

We are using Camel to send messages via ActiveMQ. In our code, we have the following configuration, it works:
#Produce(uri = IEventService.QUEUE_NAME)
private IProducer sender;
#Override
public void emit(final Event e) {
sender.emit(e);
}
Now, we want to use ActiveMQ Message Groups:
http://activemq.apache.org/message-groups.html
According to the documentation, I need to set JMSXGroupID in the message header. How do I get at the message header so that I can set this property in my emit() api?
Thanks.
-AP_
Instead of annotating #Produce to a IProducer (which you defined yourself, right?), you could do
#EndpointInject(uri = IEventService.QUEUE_NAME)
private ProducerTemplate sender;
#Override
public void emit(final Event e){
sender.sendBodyAndHeader(e,"JMSXGroupID",someId);
}
Not sure if this is ok though, or if you are using IProducer for something else.
Another solution is to send to a direct:myname endpoint. Then you have a small route from("direct:myname").to(IEventService.QUEUE_NAME)
So in this route you can easily add headers.