Azure Service Bus - Topic - Message Attribute Based Access Control - azure

I have an Azure Service Bus topic. Messages send to the topic contain application properties.
I want to set permissions on the topic based on values of one or more application properties, i.e. only when identity A sends a message with property e.g. Destination = 'service' the message is allowed to be added to the topic.
The reason behind this: assume a topic where there are multiple publishers and multiple subscribers to consume the messages from the publishers. But each message shall only go to specific consumers using filters. An actor with bad intentions or just by accident could choose a filter value and send the message therefore to wrong consumers. And I don't want to create multiple topics. I want to limit the publishers to send messages to certain consumers by limiting their permissions by message attributes with certain values on a certain topic.
Is this possible? If yes, how?

You can start a separate app which will peek the message in the queue
check for the properties and then abandon the messages.
To peek create a receiver and use receiver.ReceiveMessageAsync();
to peek the message. After this you can apply your logic to filter
the messages using the properties.
After that use AbandonAsync to abandon the message with wrong properties.
// create a reciever and peek the messages.
await using var client = new ServiceBusClient(connectionString);
//reciever
ServiceBusReceiver receiver = client.CreateReceiver(queueName) ;
// get the messages
ServiceBusReceivedMessage receivedMessage = await receiver.ReceiveMessageAsync();
// write you logic for filtering the message
//now we can abandon the message. Here "reason":" Wrong message" is optional
await receivedMessage.AbandonAsync(new Dictionary<string, object> { { "Reason", "wrong properties"} });
Refernce:
Refer the article by sfeldman
Peek messages

You can't use ServiceBus (or any message broker) the way you describe.
My advice is to consider messages only as signals to do something. But the service is entirely responsible to check if the message is legitimate or not (by calling the source service or another source of truth synchronously for example).
Imagine this scenario:
SourceApp send a DeletedEvent{CustomerId="42"}.
DestApp receives this message, and before deleting data related to this customer, makes an HTTP call to the customer service GET CustomerService/42.
If the customer is deleted => perfect
If not... something went wrong, what you described by an actor with bad intentions or just by accident
Also, if you use the same topic for a lot of applications consider using AAD authentication, with managed identity per app. Or at least forbid sharing SAS keys. Every app should have dedicated connection string, scoped to the subscription or the topic with only the access needed.

Related

Azure Service Bus conditional message locking

Is it possible to implement the following pseudo scenario with an Azure Service Bus?
I have a function that can scale out to 50 instances, it uses a service bus trigger. I would like to guarantee that related messages are only processed if an existing related message is NOT currently being processed.
Let's say I have a message (Message A) being processed by a function instance that's associated with UserID 1234. Another message (Message B) appears on the queue which is also associated with UserID 1234, the service bus should "ignore" it because a related message is already being processed. Another message (Message C) with UserID 9876 appears on the queue, this gets handled straight away because there is no in action message with UserID 9876.
Message A finishes processing and Message B is now picked up.
Currently I have a routing function which consumes the initial service bus trigger and then routes it to one of 10 functions each of which is responsible for messages where the last digit of the UserID is 0-9.
This means that if function "4" is busy with a request it won't be able to process any other requests where the UserID ends with 4, thus guaranteeing the system cannot process a related message at the same time. It does it's job but doesn't scale.
There's no conditional locking. From the description, sounds like you want to process messages associated with the same user ID, one at a time. For that, Azure Service Bus has a feature called Message Sessions.
As far as I know, azure service bus has a locking mechanism built into it. So no matter the message, if you have a single queue you are reading from, when a function picks that message up the rest of your functions would not pick up that same message. To solve the issue of not processing a duplicate message by userID I would recommend using something table storage to validate if that userID has been processed already (So in your function when you pick up a message you insert that into azure table storage before doing any processing and also have a check to see if it exists before processing).
service bus - https://learn.microsoft.com/en-us/azure/service-bus-messaging/message-transfers-locks-settlement
table storage - https://learn.microsoft.com/en-us/azure/cosmos-db/table/quickstart-dotnet?toc=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fazure%2Fstorage%2Ftables%2Ftoc.json&bc=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fazure%2Fbread%2Ftoc.json&tabs=azure-cli%2Cwindows
In summary I think the solution here would be using multiple technologies and use a central point your scaled out functions could validate against whats processed by other functions, whatever that central database is

Messages sent to Topic are getting lost if no subscriptions are specified

In Azure Service Bus, if below is the sequence of events, then all's fine -
Create Topic
Create Subscriptions inside Topic
Send Messages to Topic
With above, the subscriptions are triggered when a message is sent. This is expected.
However, if we modify the above sequence like this
Create Topic
Send Messages to Topic
Create Subscriptions inside Topic
In this case, as messages are sent to a topic whilst no subscriptions were in place, when the subscriptions are indeed created, the previously sent messages don't show up in their list. Those messages are essentially 'lost'. Am not able to see those messages in Service Bus Explorer too.
The above sequence flow is relevant because we have detached publishers and subscribers, where the publisher just sends a message and subscribers, when they come online, create the subscriptions and handle them. The order in which the publisher and subscriber come online is not guaranteed.
How can I access/process messages sent to the topic before the subscriptions are created? What happens to such messages in the first place?
Thanks
It turns out that the above behavior is by design - if no subscriptions are there, then the message is lost.
To overcome this, Azure Service Bus provides a property on topic to enable the pre-filtering of messages before they are sent. So, if no filters/subscriptions are available, it'll throw an exception
Set the option on the Topic
namespaceManager.CreateTopicAsync(new TopicDescription(topicName)
{
EnableFilteringMessagesBeforePublishing = true
});
Whilst sending the message, check for exception
try
{
await topicClient.SendAsync(brokeredMessage);
}
catch (NoMatchingSubscriptionException ex)
{
// handle the exception, maybe send it to dead letter queue using DeadLetterAsync
}

How to change message before abandon

I'd like to change a message property before abandoning. The following code don't work. I'm receiving the message with the initial Label.
message.Label = "failed";
message.Abandon();
It it possible to define that message was abandoned?
You cannot update content of an Azure Service Bus Queue message object. Frankly, if I have to update a message content, than there is something wrong in my approach.
You can, however, update the content of an Azure Storage Queue message, using the UpdateMessage method or any of its async friends. In any case, I feel wrong the general approach of touching message content!
For your purpose I suggest that you take a look at DeadLetterQueue feature of Azure SErvice Bus Queues and Topics, or the DequeueCount property of Azure Storage message to determine whether there is something wrong with the message.

Azure Service Bus Subscriber Deadletter

I've looked everywhere for information on this but can't seem to find what i'm looking for.
I've got an azure topic, with one subscription.
The handler for the subscription failed some messages, they've been put on the deadletter queue.
I can access the messages but i'm at a loss for how to restore them.
I don't want to create a copy of the message and send it to the topic. I specifically want to move it to the subscription queue it came from.
Is there any way to do this?
I don't know if this is possible.
What we mostly do to handle & resume messages, is to use the pattern of defering messages. If a handler fails a message, he can then defer the message and keep track of the messagesequence number (typically in a management / tracking database or component).
Whenever the message needs to be reprocessed (retried), a receiver can then specifically get that message (using the same defered message id) and process it again.

Send message directly to Subscription

Is it possible to send a message directly to a Subscription queue?
Scenario:
A message failed, dropped onto the deadletter, the message has been picked up manually using defer, cloned and needs to be sent to the queue it was first deadlettered on, but NOT the topic.
Can I send a message directly to a subscriber?
I've considered creating a separate retry queue per subscriber, where the handling service will also receive messages from but i'd rather not do this.
this is a similar question to the one you asked earlier: Azure Service Bus Subscriber Deadletter
this is not possible, as far as I know. the only thing you could do is to add an extra filter on every subscription that has something like SubscriptionName='SubscriptionA'. If you then want to send you deadlettered message to the specific subscription, you can add a property SubscriptionName to it, to achieve your goal. Take into account that you also have to make sure that in your original filter, you add a condition to indicate that the property SubscriptionName should not exist.
I agree however, that it would be a nice scenario to 'undeadletter' a message, so that it ends up again in his original subscription.

Resources