-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSecretRequestProcessor.cs
136 lines (111 loc) · 4.75 KB
/
SecretRequestProcessor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text;
namespace SecretDeliveryApp
{
public class SecretRequestProcessor
{
private const string UpdateSecretsDirectMethodName = "UpdateSecrets";
private readonly ILogger _logger;
private readonly ISecretProvider _secretProvider;
private readonly IIoTHubServiceClient _iotHubServiceClient;
public SecretRequestProcessor(ILogger<SecretRequestProcessor> logger,
ISecretProvider secretProvider, IIoTHubServiceClient iotHubServiceClient)
{
_logger = logger;
_secretProvider = secretProvider;
_iotHubServiceClient = iotHubServiceClient;
}
public async Task ProcessAsync(string events, CancellationToken cancellationToken = default)
{
try
{
var eventJToken = JToken.Parse(events);
var eventsToProcess = new List<JToken>();
if (eventJToken is JArray)
{
eventsToProcess = eventJToken.ToList();
}
else if (eventJToken is JObject)
{
eventsToProcess = new List<JToken> { eventJToken };
}
foreach (var @event in eventsToProcess)
{
if (IsEventValid(@event))
{
var request = GetRequestAndSubject(@event);
_logger.LogInformation($"Received Secrets Request from subject {request.Item2}");
await ProcessRequestForDeviceAsync(request.Item1, request.Item2, cancellationToken);
_logger.LogInformation($"Successfully processed Secrets Request for subject {request.Item2}");
}
}
}
catch (Exception ex)
{
_logger.LogError($"ERROR {ex.Message} processing Secrets Request");
throw;
}
}
private async Task ProcessRequestForDeviceAsync(DeviceSecretRequest request, string subject, CancellationToken cancellationToken = default)
{
var secrets = new List<Secret>();
foreach (var secretMetadata in request.Secrets)
{
var secret = await _secretProvider.GetSecretAsync(secretMetadata.Name, secretMetadata.Version, cancellationToken);
if (secret != NullSecret.Instance)
{
secrets.Add(secret);
}
else
{
_logger.LogWarning($"Secret with the name {secretMetadata.Name} and version {secretMetadata?.Version} NOT found for subject {subject}");
}
}
_logger.LogInformation($"Delivering secrets: {string.Join('|', secrets.Select(s => s.Name).ToArray())} to device {subject}");
var response = new DeviceSecretResponse(request.RequestId, secrets);
var responseAsJson = JsonConvert.SerializeObject(response);
var deviceAndModule = GetDeviceAndModuleIdFromSubject(subject);
await _iotHubServiceClient.InvokeDeviceMethodAsync(UpdateSecretsDirectMethodName, deviceAndModule.Item1,
deviceAndModule.Item2, responseAsJson, cancellationToken);
}
private bool IsEventValid(JToken jToken)
{
return jToken["type"]?.ToString() == "Microsoft.Devices.DeviceTelemetry" &&
jToken["data"]!["properties"]!["secret-request-id"] != null;
}
private (DeviceSecretRequest, string) GetRequestAndSubject(JToken jToken)
{
var body = jToken["data"]!["body"]!.ToString();
var subject = jToken["subject"]!.ToString();
if (IsBase64String(body))
{
var decodedBody = Convert.FromBase64String(body);
body = Encoding.UTF8.GetString(decodedBody);
}
var request = JsonConvert.DeserializeObject<DeviceSecretRequest>(body);
return (request!, subject!);
}
private (string, string) GetDeviceAndModuleIdFromSubject(string subject)
{
var parts = subject.Split('/');
if (parts.Length == 3)
{
return (parts[1], parts[2]);
}
else if (parts.Length == 2)
{
return (parts[1], string.Empty);
}
else
{
return (string.Empty, string.Empty);
}
}
public static bool IsBase64String(string base64)
{
var buffer = new Span<byte>(new byte[base64.Length]);
return Convert.TryFromBase64String(base64, buffer, out _);
}
}
}