Accessing authorization rules (policies) on Service Bus
When developing occasionally offline distributed systems, sometimes, you need to rethink the communication between them. Our architecture for the product we are building is composed of Azure Service Bus Topics. Those are nothing more than a fancy queue, that allows multiple readers. More specifically, in our case, the architecture assumed one topic per each tenant, and each tenant can have multiple devices. Each tenant can provision new devices at will (depending on licences, of course) and that means provisioning a new subscription on that tenant's topic.
We needed a way to create the subscription automatically, and copy the authorization rules set on the topic. Interestingly though, it proved quite a challenge to get to these rules, and the documentation online didn't really help. Anyway, this is what we came up with:
public static TopicChannel.TopicConnectionData ProvisionSubscription(TopicChannel.TopicConnectionData topicConnectionData, Guid key)
{
var serviceBusUri =
ServiceBusEnvironment.CreateServiceUri("sb", topicConnectionData.Namespace, String.Empty)
.ToString()
.Trim('/');
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(topicConnectionData.AccountName,
topicConnectionData.AccountKey);
var namespaceManager = new NamespaceManager(serviceBusUri, tokenProvider);
var topicDescription = namespaceManager.GetTopic(topicConnectionData.TopicName);
var authorizationRule =
topicDescription.Authorization.OfType<SharedAccessAuthorizationRule>().FirstOrDefault();
if (authorizationRule == null)
{
return null;
}
// create a new subscription
var filter = new SqlFilter(String.Format("{0} = '{1}' OR NOT EXISTS({0})", WellKnownConstants.Queues.PosTargetProperty, key.ToString("N")));
var newSubDescription = new SubscriptionDescription(topicConnectionData.TopicName, key.ToString("N"))
{
DefaultMessageTimeToLive = TimeSpan.FromDays(14)
};
var sd = namespaceManager.CreateSubscription(newSubDescription, filter);
var newConnectionDetails = new TopicChannel.TopicConnectionData(topicConnectionData.Namespace,
authorizationRule.KeyName, authorizationRule.PrimaryKey, sd.TopicPath, sd.Name);
return newConnectionDetails;
}
There are two interesting pieces of code here in particular:
var authorizationRule = topicDescription.Authorization.OfType<SharedAccessAuthorizationRule>().FirstOrDefault();
This actually gets the authorization rules. We only have one, so we're safe in actually getting the first one only. Obviously if that doesn't get any, we panic and return a null.
The next one is creating the SQL filter for the subscription. The documentation available, in my opinion, could be, khm, a bit clearer. Anyway, I wanted to have the ability to still target only certain devices. We can do that by setting a certain property to a value, and only the specific device would get the message. Note that this is not a security mechanism. It's just a way to filter messages.
new SqlFilter(String.Format("{0} = '{1}' OR NOT EXISTS({0})", WellKnownConstants.Queues.PosTargetProperty, key.ToString("N")));
I hope this saves someone some trouble.