I would like to apply middleware to all requests.
I can write such apply as this
consumer
.apply(
session(sessionOptions)
)
.forRoutes("*");
Will this work as well?
consumer
.apply(
session(sessionOptions)
);
It is not working. forRoutes("*") still required.
Related
I know I can use a Request guard. However, if I have a REST API with hundreds of handlers, not only it would be annoying to have to add an extra function param to all of them, but it kinda scares me that it could be easy to miss adding such a param here or there and therefore create a security hole. That's why I'd like to know if there is a way to do such a validation globally.
The documentation on Fairings mentions they can be used for global security policies:
As a general rule of thumb, only globally applicable actions should be implemented via fairings. For instance, you should not use a fairing to implement authentication or authorization (preferring to use a request guard instead) unless the authentication or authorization applies to the entire application. On the other hand, you should use a fairing to record timing and/or usage statistics or to implement global security policies.
But at the same time the docs on the on_request() callback say this:
A request callback can modify the request at will and Data::peek() into the incoming data. It may not, however, abort or respond directly to the request; these issues are better handled via request guards or via response callbacks.
So how am I supposed to return an error to the user in the case of an invalid token for example?
OK, I think I found a way...
First we create a "dummy" handler like this:
#[put("/errHnd", format = "json")]
fn err_handler() -> ApiResult {
// Here simply return an error
}
Then we attach a fairing like this:
rocket::custom(cfg)
.attach(AdHoc::on_request("OnReq", |req, _| {
// Here we validate the token and if it's not OK,
// forward the request to our "dummy" handler:
let u = Origin::parse("/errHnd").unwrap();
req.set_uri(u);
req.set_method(Method::Put);
}))
.mount("/", routes![err_handler, ...
I'm not sure that's the best way to do it, but I tested it and it seems to work. I'm open to other suggestions.
P.S. It may also be worth mentioning that if we wanted to have an exception, so as to skip the validation in the fairing, say, based on the URL, we could simply add something like this in it:
if req.uri().path() == "/let-me-in-please" {
return;
}
I am using NestJS (version 6.5, with Express platform) and I need to handle a request with a property that can either be a File or a String.
Here is the code I currently have, but I don't find a clean way to implement this.
MyAwesomeController
#Post()
#UseInterceptors(FileInterceptor('source'))
async handle(#UploadedFile() source, #Body() myDto: MyDto): Promise<any> {
//do things...
}
Am I missing something obvious or am I supposed to write my own interceptor to handle this case?
Design-wise, is this bad?
Based on the fact you're designing a REST API:
It depends what use case(s) you want to achieve: is your - client-side - flow designed to be performed in 2 steps o not ?
Can string and file params be both passed at the same time or is there only one of the two on each call ? (like if you want to update a file and its name, or some other non Multer related attributes).
When you pass a string as parameter to your endpoint call, is a file resource created / updated / deleted ? Or maybe not at all ?
Depending on the answer and the flow that you thought of, you should split both cases handling within two independent endpoints, or maybe it makes sense to handle both parameters at the same time.
If only one of the params can be passed at a time, I'd say go for two independent endpoints; you'll benefit from both maintenance and code readability.
If both params can be passed at the same time and they're related to the same resource, then it could make sense to handle both of them at once.
Hope this helps, don't hesitate to comment ;)
I have a node application using koa. It receiving webhooks from external application on specific resources.
To illustrate let say the webhook send me with POST request an object of this type :
{
'resource_id':'<SomeID>',
'resource_origin':'<SomeResourceOrigin>',
'value' : '<SomeValue>'
}
I would like to execute sequentially any resources coming from the same origin to avoid desynchronization of resources related to my execution.
I was thinking to use database as lock and use cron to sequentially executing my process for each resources of same origin.
But I'm not sure it's the most efficient method.
So my question is here :
Do you know some method/package/service allowing me to use global queues that I could implement for each origin insuring resources from same origin will be executed synchronously without making all webhooks processed sequentially ? If it do not use database it's better.
If I were you I would start by serializing the handling of all your webhooks. In other words, I suggest you handle them one at a time no matter their origin. Use a simple queue inside your nodejs application.
(Once you've convinced yourself that works correctly, you can then serialize them based on origin.)
First, structure your function (let's call it handleOneWebhook()) for handling incoming webhooks as a Promise or an async function. Then you could invoke them using code with this outline.
let busy= false
async function handleManyWebhooks (queue) {
if (busy) return
busy = true
while (queue.length > 0) {
const item = queue.shift()
await handleOneWebhook (item)
}
busy = false
}
The queue you pass to handleManyWebhooks is a simple array, where each element is the object from a POST request. You use it as a queue: push() each object to put it into the queue, and shift() to remove it.
Then, whenever you receive a webhook POST object you use code with this outline.
const queue = []
...
function handlePostObject (postObject) {
queue.push(postObject)
handleManyWebooks (queue)
}
Even though you call handleManyWebhooks once for each incoming object, the busy flag makes sure it handles only one at a time.
Notice this is a very simple solution. Once you have it working correctly, two possible refinements suggest themselves.
Use something more efficient for your queue than a simple array. shift() is not very fast.
Create a separate queue object with its own busy flag for each separate origin. Then you will be able to parallelize the handling of webhooks from different origins while still serializing the stream of webhooks from each origin.
Solution I decide to use
Small brief of the post discussion
As Ivan Rubinson let me know my problem is just a producer-consumer problem.
So I finally chose to use RabbitMQ because I have a huge amount of webhook to process. For peoples having a small amount of request to process and do not want use external tools O. Jones answer is a real good way to solve the problem.
Solution design
I finally install and configure a RabbitMQ server, then I created for each origin of my web-hooks one queue.
Producer
On the producer side when I receive the web-hook data I send a message to the queue corresponding to the origin of my web-hook with serialized information needed to process in fact id of the row in the Database to make messages as light as possible.
Consumer
On the consumer side I create a consumer function for each origin queue and set the fetch policy to one to process message one by one in each queue finally I set the channel policy to wait an acknowledgement message before to send the next message . Wit this configuration consumers proceed message by message and solve the initial problem.
Implementation
Producer
async function create(){
await amqp.connect(RBMQ_CONNECTION_STRING).then(async (conn)=>{
await conn.createChannel().then(async (ch)=>{
global.channel_publisher=ch;
});
});
}
async function sendtask(queue,task){
if(!global.channel_publisher){
await create();
}
global.channel_publisher.assertQueue(queue).then((ok)=>{
global.channel_publisher.sendToQueue(queue, Buffer.from(task));
});
}
I use the sendtask(queue,task) function at the place I received my web-hook
Consumer
async function create(){
await amqp.connect(RBMQ_CONNECTION_STRING).then(async (conn)=>{
await conn.createChannel().then(async (ch)=>{
ch.prefetch(1);
global.channel_consumer=ch;
});
});
}
async function consumeTask(queue){
if(!global.channel_consumer){
await create();
}
global.channel_consumer.assertQueue(queue).then((ok)=>{
global.channel_consumer.consume(queue,(message)=>{
const args=message.content.toString().split(';');
await processWebhooks(args);
global.channel_consumer.ack(message);
});
});
}
I use the consumeTask(queue) when I had to process a new origin of web-hooks. Also I use it for initialize my application with all known origins in the database.
Is there a way to hook up a function or task to sails response , before emitting the functions, like for example , i have several res.ok(object_output); based on some conditions and in separate controllers , i want to intercept each res.ok (just like policies) to do something before returning the response.
Put it in api/responses/ok.js.
I am building a nodejs + express RESTful server, and am trying to leverage middleware to ease authorization of specific actions.
What I am trying to achieve is to pass parameters to my authorization middleware functions. I was wondering if it is at all possible to do this in the routes or if I have to extract the parameters in the middleware function. I was hoping to avoid that behavior as I have been *hum* not entirely consistent in my URL parameter names.
What I would like to do is something like this:
router.get(
'/:productId',
auth.can_get_this_product(productId), // Pass the productId here
controller.perform_action_that_requires_authorization
);
But this is not possible. Because I have other routes where the names of parameters might not be the same (ie: router.get(/:p_id, auth.can_get_thi...). I realize I should probably just go back and make sure that my parameter names are consistent everywhere and retrieve the parameters in the middleware using req.param('productId')but I am curious if it would be at all possible.
Well, I suppose you can pass the params hash key and then use that.
router.get(
'/:productId',
auth.can_get_this_product('productId'), // Pass the productId *KEY* here
controller.perform_action_that_requires_authorization
);
//....
function can_get_this_product(productIdKey) {
var productId = req.params[productIdKey];
//....
}
But of course, we both know you should just bite the bullet and refactor those names.