Created
June 23, 2014 16:54
-
-
Save chgeuer/aa82a7c5bd41ef4cc507 to your computer and use it in GitHub Desktop.
Shows how to configure access control service with symmetric keys for device identities
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace IoTDemo | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Configuration; | |
using System.Linq; | |
using System.Security.Claims; | |
using System.Security.Cryptography; | |
using System.Text; | |
using System.Threading; | |
using Microsoft.ServiceBus; | |
using Microsoft.ServiceBus.Messaging; | |
using Common.ACS.Management; | |
/* | |
* This sample uses the Access Control Service management library from here: | |
* https://github.com/chgeuer/Windows-Azure-AD-Access-0dcde385/tree/master/C%23/Management/ManagementService/Common | |
* | |
* | |
* https://dsservicebus-sb.accesscontrol.windows.net/v2/mgmt/service/$metadata | |
*/ | |
// <appSettings> | |
// <add key="ServiceBusNamespace" value="dsservicebus"/> | |
// <add key="ServiceBusManagementServiceIdentityName" value="owner"/> | |
// <add key="ServiceBusManagementServiceIdentityKey" value="hfkjdshfkjhdsjfhkdshfkshk==="/> | |
// <add key="AccessControlServiceManagementServiceIdentityName" value="SBManagementClient"/> | |
// <add key="AccessControlServiceManagementServiceIdentityKey" value="hfkjdshfkjhdsjfhkdshfkshk==="/> | |
//</appSettings> | |
internal class Program | |
{ | |
private static readonly string deviceName = "46005d4e-3783-469e-834c-89af0980db9a"; | |
private static readonly string deviceKey = "59bfi8Qy+DV/1qAD8VbS1Ew9HI3KDsr4QpJuAzb1iA0="; | |
private static void Main(string[] args) | |
{ | |
var deviceId = new DeviceId(deviceName); | |
var client = SampleConfiguration.GetAdminQueueClientFromSampleCredentials(deviceId); | |
for (int i = 0; i < 5; i++) | |
{ | |
client.Send(new BrokeredMessage(string.Format("Timebombed {0} ", DateTime.Now.ToString())) | |
{ | |
TimeToLive = TimeSpan.FromSeconds(2) | |
}); | |
Console.WriteLine("Sent message"); | |
} | |
client.Send(new BrokeredMessage(string.Format("Not timebombed {0} ", DateTime.Now.ToString())) { }); | |
Thread.Sleep(TimeSpan.FromSeconds(5)); | |
var pingClient = deviceId.CreateDeviceQueueClient(SampleConfiguration.ServiceBusNamespace, Convert.FromBase64String(deviceKey), | |
ReceiveMode.PeekLock); | |
var tempMsg = pingClient.Receive(TimeSpan.FromSeconds(1)); | |
if (tempMsg != null) | |
{ | |
Console.WriteLine("Peeked into message, Abandon: " + tempMsg.GetBody<string>()); | |
tempMsg.Abandon(); | |
} | |
Console.WriteLine("Checking deadletter queue"); | |
var deadClient = deviceId.CreateDeviceQueueClient(SampleConfiguration.ServiceBusNamespace, Convert.FromBase64String(deviceKey), | |
ReceiveMode.ReceiveAndDelete, QueueType.DeadLetter); | |
BrokeredMessage msg; | |
while ((msg = deadClient.Receive(TimeSpan.FromSeconds(1))) != null) | |
{ | |
Console.WriteLine("Deadletter Received: " + msg.GetBody<string>()); | |
} | |
Console.WriteLine("Start receiving"); | |
var receiveClient = deviceId.CreateDeviceQueueClient(SampleConfiguration.ServiceBusNamespace, Convert.FromBase64String(deviceKey)); | |
while ((msg = receiveClient.Receive(TimeSpan.FromSeconds(3))) != null) | |
{ | |
Console.WriteLine("Received: " + msg.GetBody<string>()); | |
} | |
Console.WriteLine("Finished receiving"); | |
} | |
private static void MainSetup(string[] args) | |
{ | |
ManagementService acsMgmt = SampleConfiguration.CreateAccessControlServiceManagement(); | |
NamespaceManager queueMgmt = SampleConfiguration.CreateServiceBusManagement(); | |
string serviceBusNamespace = SampleConfiguration.ServiceBusNamespace; | |
ServiceBusExtensions.DeleteAllAssets(acsMgmt, queueMgmt); | |
DeviceId deviceId = new DeviceId { Id = Guid.NewGuid() }; | |
byte[] key = KeyGen.CreateNewKey(); | |
acsMgmt.Provision(serviceBusNamespace, deviceId, key, queueMgmt); | |
var client = SampleConfiguration.GetAdminQueueClientFromSampleCredentials(deviceId); | |
for (int i = 0; i < 5; i++) | |
{ | |
var body = string.Format("Test message {0}", i); | |
BrokeredMessage message = new BrokeredMessage(body) | |
{ | |
ContentType = "text/plain", | |
TimeToLive = TimeSpan.FromSeconds(2), | |
To = "Dude" | |
}; | |
message.Properties["TestProperty"] = "TestValue"; | |
message.Properties["Message number"] = i; | |
client.Send(message); | |
} | |
Console.WriteLine("Sent all messages"); | |
Thread.Sleep(TimeSpan.FromSeconds(3)); | |
try | |
{ | |
string connectionString = deviceId.GetServiceBusAmqpConnectionString(serviceBusNamespace, key); | |
var deadletterQueuePath = deviceId.GetDeadLetterQueuePath(); | |
var receiveClient = QueueClient.CreateFromConnectionString(connectionString, deadletterQueuePath, ReceiveMode.ReceiveAndDelete); | |
while (true) | |
{ | |
var brokeredMessage = receiveClient.Receive(); | |
if (brokeredMessage != null) | |
{ | |
Console.WriteLine(brokeredMessage.GetBody<string>()); | |
// brokeredMessage.Complete(); | |
} | |
} | |
} | |
finally | |
{ | |
} | |
} | |
} | |
public class DeviceId | |
{ | |
public Guid Id { get; set; } | |
public DeviceId() { } | |
public DeviceId(Guid id) { this.Id = id; } | |
public DeviceId(string id) { this.Id = Guid.Parse(id); } | |
public override string ToString() | |
{ | |
return this.Id.ToString(); | |
} | |
} | |
internal static class KeyGen | |
{ | |
private static readonly RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); | |
internal static byte[] CreateNewKey() | |
{ | |
byte[] key = new byte[32]; | |
rng.GetBytes(key); | |
return key; | |
} | |
} | |
public static class SampleConfiguration | |
{ | |
public static string ServiceBusNamespace { get { return ConfigurationManager.AppSettings["ServiceBusNamespace"]; } } | |
public static string ServiceBusManagementServiceIdentityName { get { return ConfigurationManager.AppSettings["ServiceBusManagementServiceIdentityName"]; } } | |
public static string ServiceBusManagementServiceIdentityKey { get { return ConfigurationManager.AppSettings["ServiceBusManagementServiceIdentityKey"]; } } | |
private static string GetServiceBusManagementConnectionString() | |
{ | |
return string.Format( | |
"Endpoint=https://{0}.servicebus.windows.net/;SharedSecretIssuer={1};SharedSecretValue={2}", | |
SampleConfiguration.ServiceBusNamespace, | |
SampleConfiguration.ServiceBusManagementServiceIdentityName, | |
SampleConfiguration.ServiceBusManagementServiceIdentityKey); | |
} | |
public static NamespaceManager CreateServiceBusManagement() | |
{ | |
var connectionString = GetServiceBusManagementConnectionString(); | |
return NamespaceManager.CreateFromConnectionString(connectionString); | |
} | |
public static string AccessControlServiceManagementNamespace { get { return SampleConfiguration.ServiceBusNamespace + "-sb"; } } | |
public static string AccessControlServiceManagementServiceIdentityName { get { return ConfigurationManager.AppSettings["AccessControlServiceManagementServiceIdentityName"]; } } | |
public static string AccessControlServiceManagementServiceIdentityKey { get { return ConfigurationManager.AppSettings["AccessControlServiceManagementServiceIdentityKey"]; } } | |
public static ManagementService CreateAccessControlServiceManagement() | |
{ | |
return new ManagementService( | |
SampleConfiguration.AccessControlServiceManagementNamespace, | |
SampleConfiguration.AccessControlServiceManagementServiceIdentityName, | |
SampleConfiguration.AccessControlServiceManagementServiceIdentityKey); | |
} | |
public static QueueClient GetAdminQueueClientFromSampleCredentials(DeviceId deviceId, | |
ReceiveMode receiveMode = ReceiveMode.ReceiveAndDelete, QueueType queueType = QueueType.Regular) | |
{ | |
return deviceId.CreateAdminQueueClient( | |
SampleConfiguration.ServiceBusNamespace, | |
SampleConfiguration.ServiceBusManagementServiceIdentityName, | |
Convert.FromBase64String(SampleConfiguration.ServiceBusManagementServiceIdentityKey), | |
receiveMode, queueType); | |
} | |
} | |
public static class ServiceBusExtensions | |
{ | |
public static string GetQueuePath(this DeviceId deviceId) | |
{ | |
return string.Format("devices/{0}", deviceId).ToLower(); | |
} | |
public static string GetDeadLetterQueuePath(this DeviceId deviceId) | |
{ | |
return QueueClient.FormatDeadLetterPath(deviceId.GetQueuePath()); | |
} | |
public static string GetQueueUrl(this DeviceId deviceId, string serviceBusNamespace) | |
{ | |
return string.Format("http://{0}.servicebus.windows.net/{1}", serviceBusNamespace, deviceId.GetQueuePath()); | |
} | |
public static string GetRuleDescription(this string principal, string permission) | |
{ | |
return string.Format("{0} can {1}", principal, permission); | |
} | |
public static string GetRuleGroupName(this DeviceId deviceId, string serviceBusNamespace) | |
{ | |
return string.Format("Default Rule Group for {0}", deviceId.GetQueueUrl(serviceBusNamespace)); | |
} | |
public static string GetServiceIdentityName(this DeviceId deviceId) | |
{ | |
return string.Format("device-{0}", deviceId); | |
} | |
public static string GetServiceBusAmqpConnectionString(this DeviceId deviceId, string serviceBusNamespace, byte[] key) | |
{ | |
return string.Format( | |
"Endpoint=sb://{0}.servicebus.windows.net/;SharedSecretIssuer={1};SharedSecretValue={2};TransportType=Amqp", | |
serviceBusNamespace, deviceId.GetServiceIdentityName(), Convert.ToBase64String(key)); | |
} | |
internal static class Operation | |
{ | |
internal const string Manage = "Manage"; | |
internal const string Listen = "Listen"; | |
internal const string Send = "Send"; | |
} | |
public static byte[] Provision(this ManagementService acsMgmt, string serviceBusNamespace, DeviceId deviceId, byte[] keyBytes, NamespaceManager queueMgmt) | |
{ | |
#region Identity and keys | |
var serviceIdentityName = deviceId.GetServiceIdentityName(); | |
var serviceIdentity = new ServiceIdentity | |
{ | |
Name = serviceIdentityName, | |
Description = serviceIdentityName | |
}; | |
acsMgmt.AddToServiceIdentities(serviceIdentity); | |
string serviceIdentityKeyDisplayName = "key for " + serviceIdentityName; | |
DateTime keyValidityStartDate = DateTime.UtcNow; | |
DateTime keyValidityEndDate = DateTime.MaxValue; | |
byte[] keyBytesAsPasswordBytes = Encoding.UTF8.GetBytes(Convert.ToBase64String(keyBytes)); | |
ServiceIdentityKey symmetricKey = new ServiceIdentityKey | |
{ | |
DisplayName = serviceIdentityKeyDisplayName, | |
ServiceIdentityId = serviceIdentity.Id, | |
Usage = ServiceKeyUsage.Signing.ToString(), | |
StartDate = keyValidityStartDate, | |
EndDate = keyValidityEndDate, | |
Type = ServiceKeyType.Symmetric.ToString(), | |
Value = keyBytes | |
}; | |
acsMgmt.AddRelatedObject(serviceIdentity, "ServiceIdentityKeys", symmetricKey); | |
ServiceIdentityKey passwordKey = new ServiceIdentityKey | |
{ | |
DisplayName = serviceIdentityKeyDisplayName, | |
ServiceIdentityId = serviceIdentity.Id, | |
Usage = ServiceKeyUsage.Signing.ToString(), | |
StartDate = keyValidityStartDate, | |
EndDate = keyValidityEndDate, | |
Type = ServiceKeyType.Password.ToString(), | |
Value = keyBytesAsPasswordBytes | |
}; | |
acsMgmt.AddRelatedObject(serviceIdentity, "ServiceIdentityKeys", passwordKey); | |
// acsMgmt.SaveChangesBatch(); | |
#endregion | |
#region RuleGroup and Rules | |
string ruleGroupName = deviceId.GetRuleGroupName(serviceBusNamespace); | |
RuleGroup ruleGroup = acsMgmt.CreateRuleGroup(ruleGroupName); | |
Issuer localAuthority = acsMgmt.Issuers.Where(_ => _.Name == "LOCAL AUTHORITY").FirstOrDefault(); | |
var perms = new List<Tuple<string, string>> | |
{ | |
new Tuple<string, string>(deviceId.GetServiceIdentityName(), Operation.Listen), | |
new Tuple<string, string>("owner", Operation.Manage), | |
new Tuple<string, string>("owner", Operation.Send) | |
}; | |
foreach (var perm in perms) | |
{ | |
string principal = perm.Item1; | |
string permission = perm.Item2; | |
string ruleDescription = principal.GetRuleDescription(permission); | |
var rule = new Rule | |
{ | |
Description = ruleDescription, | |
IssuerId = localAuthority.Id, | |
InputClaimType = ClaimTypes.NameIdentifier, | |
InputClaimValue = principal, | |
OutputClaimType = "net.windows.servicebus.action", | |
OutputClaimValue = permission | |
}; | |
acsMgmt.AddRelatedObject(ruleGroup, "Rules", rule); | |
} | |
acsMgmt.SaveChangesBatch(); | |
#endregion | |
#region RelyingParty | |
RelyingParty relyingParty = new RelyingParty | |
{ | |
Name = string.Format("Queue for {0}", deviceId.GetServiceIdentityName()), | |
DisplayName = string.Format("Queue for {0}", deviceId.GetServiceIdentityName()), | |
TokenType = "SWT", | |
TokenLifetime = (int)TimeSpan.FromHours(1).TotalSeconds, | |
AsymmetricTokenEncryptionRequired = false | |
}; | |
acsMgmt.AddToRelyingParties(relyingParty); | |
RelyingPartyAddress realmAddress = new RelyingPartyAddress() | |
{ | |
Address = deviceId.GetQueueUrl(serviceBusNamespace), | |
EndpointType = RelyingPartyAddressType.Realm.ToString(), | |
}; | |
acsMgmt.AddRelatedObject(relyingParty, "RelyingPartyAddresses", realmAddress); | |
RelyingPartyRuleGroup relyingPartyRuleGroup = new RelyingPartyRuleGroup | |
{ | |
RuleGroupId = ruleGroup.Id, | |
RelyingPartyId = relyingParty.Id | |
}; | |
acsMgmt.AddRelatedObject(relyingParty, "RelyingPartyRuleGroups", relyingPartyRuleGroup); | |
var identityProvider = acsMgmt.IdentityProviders.Where(_ => _.DisplayName == "Windows Live ID").FirstOrDefault(); | |
RelyingPartyIdentityProvider relyingPartyIdentityProvider = new RelyingPartyIdentityProvider | |
{ | |
RelyingPartyId = relyingParty.Id, | |
IdentityProviderId = identityProvider.Id | |
}; | |
acsMgmt.AddRelatedObject(relyingParty, "RelyingPartyIdentityProviders", relyingPartyIdentityProvider); | |
acsMgmt.SaveChangesBatch(); | |
#endregion | |
#region Queue creation | |
string queueName = deviceId.GetQueuePath(); | |
queueMgmt.CreateQueue(new QueueDescription(queueName) | |
{ | |
DefaultMessageTimeToLive = TimeSpan.FromMinutes(1), | |
EnableDeadLetteringOnMessageExpiration = true, | |
EnableBatchedOperations = true | |
}); | |
#endregion | |
return keyBytes; | |
} | |
public static void DeleteAllAssets(this ManagementService acsMgmt, NamespaceManager queueMgmt) | |
{ | |
queueMgmt.GetQueues().Where(_ => _.Path.Contains("devices/")).ToList().ForEach(_ => queueMgmt.DeleteQueue(_.Path)); | |
acsMgmt | |
.ServiceIdentities | |
.Where(_ => _.Name.StartsWith("device-")) | |
.ToList() | |
.ForEach(_ => acsMgmt.DeleteServiceIdentityIfExists(_.Name)); | |
acsMgmt.SaveChangesBatch(); | |
var ruleGroupPrefix = string.Format("Default Rule Group for http://{0}.servicebus.windows.net/devices/", SampleConfiguration.ServiceBusNamespace); | |
acsMgmt | |
.RuleGroups | |
.Where(_ => _.Name.StartsWith(ruleGroupPrefix)) | |
.ToList() | |
.ForEach(_ => acsMgmt.DeleteRuleGroupByNameIfExists(_.Name)); | |
acsMgmt.SaveChangesBatch(); | |
acsMgmt | |
.RelyingParties | |
.Where(_ => _.Name.StartsWith("Queue for device-")) | |
.ToList() | |
.ForEach(_ => acsMgmt.DeleteRelyingPartyByNameIfExists(_.Name)); | |
acsMgmt.SaveChangesBatch(); | |
} | |
public static QueueClient CreateDeviceQueueClient(this DeviceId deviceId, string serviceBusNamespace, byte[] keyBytes, | |
ReceiveMode receiveMode = ReceiveMode.ReceiveAndDelete, | |
QueueType queueType = QueueType.Regular) | |
{ | |
string connectionString = deviceId.GetServiceBusAmqpConnectionString(serviceBusNamespace, keyBytes); | |
string queuePath = (queueType == QueueType.Regular) ? deviceId.GetQueuePath() : deviceId.GetDeadLetterQueuePath(); | |
return QueueClient.CreateFromConnectionString(connectionString, queuePath, receiveMode); | |
} | |
public static QueueClient CreateAdminQueueClient(this DeviceId deviceId, string serviceBusNamespace, string ownerName, byte[] keyBytes, | |
ReceiveMode receiveMode = ReceiveMode.ReceiveAndDelete, | |
QueueType queueType = QueueType.Regular) | |
{ | |
string connectionString = string.Format( | |
"Endpoint=sb://{0}.servicebus.windows.net/;SharedSecretIssuer={1};SharedSecretValue={2};TransportType=Amqp", | |
serviceBusNamespace, ownerName, Convert.ToBase64String(keyBytes)); | |
string queuePath = (queueType == QueueType.Regular) ? deviceId.GetQueuePath() : deviceId.GetDeadLetterQueuePath(); | |
return QueueClient.CreateFromConnectionString(connectionString, queuePath, receiveMode); | |
} | |
} | |
public enum QueueType | |
{ | |
Regular = 0, | |
DeadLetter | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment