From 73699079fd02e105e95f604ad9dc94dd3b9dd87a Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 20 Apr 2022 10:24:37 -0700 Subject: [PATCH 01/25] Working on Webhook Queue Changes. --- src/ApiService/ApiService/QueueWebhooks.cs | 29 ++++ .../onefuzzlib/WebhookMessageOperations.cs | 152 ++++++++++++++++++ .../onefuzzlib/WebhookOperations.cs | 113 ------------- 3 files changed, 181 insertions(+), 113 deletions(-) create mode 100644 src/ApiService/ApiService/QueueWebhooks.cs create mode 100644 src/ApiService/ApiService/onefuzzlib/WebhookMessageOperations.cs diff --git a/src/ApiService/ApiService/QueueWebhooks.cs b/src/ApiService/ApiService/QueueWebhooks.cs new file mode 100644 index 0000000000..65b5b9a3f1 --- /dev/null +++ b/src/ApiService/ApiService/QueueWebhooks.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.Azure.Functions.Worker; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; + +namespace Microsoft.OneFuzz.Service; + +public class QueueWebhooks +{ + private readonly ILogTracerFactory _loggerFactory; + + public QueueWebhooks(ILogTracerFactory loggerFactory) + { + _loggerFactory = loggerFactory; + } + + [Function("QueueWebhooks")] + public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")] string msg) + { + var log = _loggerFactory.MakeLogTracer(Guid.NewGuid()); + + log.Info($"Webhook Message Queued: {msg}"); + + var obj = JsonSerializer.Deserialize(msg, EntityConverter.GetJsonSerializerOptions()).EnsureNotNull($"wrong data {msg}"); ; + + // WebhookMessageLog.process_from_queue(obj) + } +} \ No newline at end of file diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookMessageOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookMessageOperations.cs new file mode 100644 index 0000000000..5dae4022cc --- /dev/null +++ b/src/ApiService/ApiService/onefuzzlib/WebhookMessageOperations.cs @@ -0,0 +1,152 @@ +using ApiService.OneFuzzLib.Orm; +using Microsoft.OneFuzz.Service; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ApiService.OneFuzzLib; + + +public interface IWebhookMessageLogOperations : IOrm +{ + +} + + +public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations +{ + record WebhookMessageQueueObj( + Guid WebhookId, + Guid EventId + ); + + private readonly IQueue _queue; + private readonly ILogTracerFactory _loggerFactory; + public WebhookMessageLogOperations(IStorage storage, IQueue queue, IWebhookMessageLogOperations webhookMessage, ILogTracerFactory loggerFactory) : base(storage) + { + _queue = queue; + _webhookMessage = webhookMessage; + _loggerFactory = loggerFactory; + } + + + public async Task QueueWebhook(WebhookMessageLog webhookLog) + { + var log = _loggerFactory.MakeLogTracer(Guid.NewGuid()); + var obj = new WebhookMessageQueueObj(webhookLog.WebhookId, webhookLog.EventId); + + TimeSpan? visibilityTimeout = webhookLog.State switch + { + WebhookMessageState.Queued => TimeSpan.Zero, + WebhookMessageState.Retrying => TimeSpan.FromSeconds(30), + _ => null + }; + + if (visibilityTimeout == null) + { + log.AddTags( + new[] { + ("WebhookId", webhookLog.WebhookId.ToString()), + ("EventId", webhookLog.EventId.ToString()) } + ). + Error($"invalid WebhookMessage queue state, not queuing. {webhookLog.WebhookId}:{webhookLog.EventId} - {webhookLog.State}"); + } + else + { + await _queue.QueueObject("webhooks", obj, StorageType.Config, visibilityTimeout: visibilityTimeout); + } + } + + + public void process() + { + if (_webhookMessage.State == WebhookMessageState.Failed || _webhookMessage.State == WebhookMessageState.Succeeded) + { + log.AddTags( + new[] { + ("WebhookId", _webhookMessage.WebhookId.ToString()), + ("EventId", _webhookMessage.EventId.ToString()) } + ). + Error($"webhook message already handled. {_webhookMessage.WebhookId}:{_webhookMessage.EventId}"); + } + + _webhookMessage.TryCount++; + + log.info($"sending webhook: {_webhookMessage.WebhookId}:{_webhookMessage.EventId}") + + } + + + public async Task ProcessFromQueue(WebhookMessageQueueObj webhookMessage) + { + message = await _webhookMessage.get(webhookMessage.WebhookId, webhookMessage.EventId); + + if (message == null) + { + log.AddTags( + new[] { + ("WebhookId", webhookMessage.WebhookId.ToString()), + ("EventId", webhookMessage.EventId.ToString()) } + ). + Error($"Webhook with webhookId: {webhookMessage.WebhookId} and eventId: {webhookMessage.EventId} not found."); + ) + } + + message.process(); + } + + private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) + { + throw new NotImplementedException(); + } +} + + +public interface IWebhookOperations +{ + Task SendEvent(EventMessage eventMessage); +} + +public class WebhookOperations : Orm, IWebhookOperations +{ + private readonly IWebhookMessageLogOperations _webhookMessageLogOperations; + public WebhookOperations(IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations) + : base(storage) + { + _webhookMessageLogOperations = webhookMessageLogOperations; + } + + async public Task SendEvent(EventMessage eventMessage) + { + await foreach (var webhook in GetWebhooksCached()) + { + if (!webhook.EventTypes.Contains(eventMessage.EventType)) + { + continue; + } + await AddEvent(webhook, eventMessage); + } + } + + async private Task AddEvent(Webhook webhook, EventMessage eventMessage) + { + var message = new WebhookMessageLog( + EventId: eventMessage.EventId, + EventType: eventMessage.EventType, + Event: eventMessage.Event, + InstanceId: eventMessage.InstanceId, + InstanceName: eventMessage.InstanceName, + WebhookId: webhook.WebhookId + ); + + await _webhookMessageLogOperations.Replace(message); + } + + + //todo: caching + public IAsyncEnumerable GetWebhooksCached() + { + return QueryAsync(); + } + +} diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index ebc4bb1357..e69de29bb2 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -1,113 +0,0 @@ -using ApiService.OneFuzzLib.Orm; -using Microsoft.OneFuzz.Service; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace ApiService.OneFuzzLib; - - -public interface IWebhookMessageLogOperations : IOrm -{ - -} - - -public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations -{ - record WebhookMessageQueueObj( - Guid WebhookId, - Guid EventId - ); - - private readonly IQueue _queue; - private readonly ILogTracerFactory _loggerFactory; - public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracerFactory loggerFactory) : base(storage) - { - _queue = queue; - _loggerFactory = loggerFactory; - } - - - public async Task QueueWebhook(WebhookMessageLog webhookLog) - { - var log = _loggerFactory.MakeLogTracer(Guid.NewGuid()); - var obj = new WebhookMessageQueueObj(webhookLog.WebhookId, webhookLog.EventId); - - TimeSpan? visibilityTimeout = webhookLog.State switch - { - WebhookMessageState.Queued => TimeSpan.Zero, - WebhookMessageState.Retrying => TimeSpan.FromSeconds(30), - _ => null - }; - - if (visibilityTimeout == null) - { - log.AddTags( - new[] { - ("WebhookId", webhookLog.WebhookId.ToString()), - ("EventId", webhookLog.EventId.ToString()) } - ). - Error($"invalid WebhookMessage queue state, not queuing. {webhookLog.WebhookId}:{webhookLog.EventId} - {webhookLog.State}"); - } - else - { - await _queue.QueueObject("webhooks", obj, StorageType.Config, visibilityTimeout: visibilityTimeout); - } - } - - private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) - { - throw new NotImplementedException(); - } -} - - -public interface IWebhookOperations -{ - Task SendEvent(EventMessage eventMessage); -} - -public class WebhookOperations : Orm, IWebhookOperations -{ - private readonly IWebhookMessageLogOperations _webhookMessageLogOperations; - public WebhookOperations(IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations) - : base(storage) - { - _webhookMessageLogOperations = webhookMessageLogOperations; - } - - async public Task SendEvent(EventMessage eventMessage) - { - await foreach (var webhook in GetWebhooksCached()) - { - if (!webhook.EventTypes.Contains(eventMessage.EventType)) - { - continue; - } - await AddEvent(webhook, eventMessage); - } - } - - async private Task AddEvent(Webhook webhook, EventMessage eventMessage) - { - var message = new WebhookMessageLog( - EventId: eventMessage.EventId, - EventType: eventMessage.EventType, - Event: eventMessage.Event, - InstanceId: eventMessage.InstanceId, - InstanceName: eventMessage.InstanceName, - WebhookId: webhook.WebhookId - ); - - await _webhookMessageLogOperations.Replace(message); - } - - - //todo: caching - public IAsyncEnumerable GetWebhooksCached() - { - return QueryAsync(); - } - -} From 0a4d45ee084ab7cb217eef7a9c74da1b5c1b98f3 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 21 Apr 2022 09:43:12 -0700 Subject: [PATCH 02/25] Initial Push w/ Webhook Work. --- src/ApiService/ApiService/ApiService.csproj | 1 + .../ApiService/OneFuzzTypes/Primitives.cs | 0 .../ApiService/OneFuzzTypes/Webhooks.cs | 3 - .../ApiService/onefuzzlib/Containers.cs | 85 + src/ApiService/ApiService/onefuzzlib/Creds.cs | 29 + .../ApiService/onefuzzlib/Storage.cs | 37 + .../onefuzzlib/WebhookMessageLogOperations.cs | 151 ++ .../onefuzzlib/WebhookMessageOperations.cs | 152 -- .../onefuzzlib/WebhookOperations.cs | 95 +- src/ApiService/ApiService/packages.lock.json | 2086 +++++++++-------- 10 files changed, 1380 insertions(+), 1259 deletions(-) create mode 100644 src/ApiService/ApiService/OneFuzzTypes/Primitives.cs create mode 100644 src/ApiService/ApiService/onefuzzlib/Containers.cs create mode 100644 src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs delete mode 100644 src/ApiService/ApiService/onefuzzlib/WebhookMessageOperations.cs diff --git a/src/ApiService/ApiService/ApiService.csproj b/src/ApiService/ApiService/ApiService.csproj index ec2bc65a71..93158ad4e5 100644 --- a/src/ApiService/ApiService/ApiService.csproj +++ b/src/ApiService/ApiService/ApiService.csproj @@ -24,6 +24,7 @@ + diff --git a/src/ApiService/ApiService/OneFuzzTypes/Primitives.cs b/src/ApiService/ApiService/OneFuzzTypes/Primitives.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs b/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs index 16d453728c..a1f79b12c2 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs @@ -30,9 +30,6 @@ public record WebhookMessageEventGrid( [property: JsonConverter(typeof(BaseEventConverter))] BaseEvent data); - -// TODO: This should inherit from Entity Base ? no, since there is -// a table WebhookMessaageLog public record WebhookMessageLog( [RowKey] Guid EventId, EventType EventType, diff --git a/src/ApiService/ApiService/onefuzzlib/Containers.cs b/src/ApiService/ApiService/onefuzzlib/Containers.cs new file mode 100644 index 0000000000..e974cb6b73 --- /dev/null +++ b/src/ApiService/ApiService/onefuzzlib/Containers.cs @@ -0,0 +1,85 @@ +using System; +using Azure.Storage; +using Azure.Storage.Blobs; +using Container = System.String; + +namespace Microsoft.OneFuzz.Service; + +public interface IContainers +{ + public Async.Task GetBlob(Container container, string name, StorageType storageType); +} + +public class Containers : IContainers +{ + + private ILogTracer _log; + private IStorage _storage; + + public Containers(ILogTracer log, IStorage storage) + { + _log = log; + _storage = storage; + } + + private Uri GetUrl(string accountName) + { + return new Uri($"https://{accountName}.blob.core.windows.net/"); + } + + + private BlobServiceClient GetBlobService(string accountId) + { + _log.Info($"getting blob container (account_id: {accountId}"); + var (accountName, accountKey) = _storage.GetStorageAccountNameAndKey(accountId); + if (accountName == null || accountKey == null) + { + throw new System.Exception($"Could not find storage account with accountId: {accountId}"); + } + var accountUrl = GetUrl(accountName); + var service = new BlobServiceClient(serviceUri:accountUrl, credential: new StorageSharedKeyCredential(accountName, accountKey)); + + return service; + } + private BlobContainerClient? FindContainer(Container container, StorageType storageType) + { + var accounts = _storage.GetAccounts(storageType); + + // check secondary accounts first by searching in reverse. + // + // By implementation, the primary account is specified first, followed by + // any secondary accounts. + // + // Secondary accounts, if they exist, are preferred for containers and have + // increased IOP rates, this should be a slight optimization + accounts.Reverse(); + foreach (var account in accounts) + { + var client = GetBlobService(account).GetBlobContainerClient(container); + if (client.Exists()) + { + return client; + } + } + return null; + } + public async Async.Task GetBlob(Container container, string name, StorageType storageType) + { + var client = FindContainer(container, storageType); + if (client == null) + { + return null; + } + + try + { + // let! r = client.GetBlobClient(name).DownloadContentAsync() + // return Some(r.Value.Content.ToArray()) + var content = await client.GetBlobClient(name).DownloadContentAsync(); + return content.Value.Content.ToArray(); + } catch (Exception) + { + return null; + } + } +} \ No newline at end of file diff --git a/src/ApiService/ApiService/onefuzzlib/Creds.cs b/src/ApiService/ApiService/onefuzzlib/Creds.cs index e98f5d05be..3831526ffe 100644 --- a/src/ApiService/ApiService/onefuzzlib/Creds.cs +++ b/src/ApiService/ApiService/onefuzzlib/Creds.cs @@ -1,5 +1,6 @@ using Azure.Identity; using Azure.Core; +using System; namespace Microsoft.OneFuzz.Service; @@ -12,11 +13,21 @@ public interface ICreds public string GetBaseResourceGroup(); public ResourceIdentifier GetResourceGroupResourceIdentifier(); + + public string GetInstanceName(); + + public Async.Task GetInstanceId(); } public class Creds : ICreds { + private IContainers _containers; + + public Creds(IContainers containers) + { + _containers = containers; + } // TODO: @cached public DefaultAzureCredential GetIdentity() { @@ -47,4 +58,22 @@ public ResourceIdentifier GetResourceGroupResourceIdentifier() ?? throw new System.Exception("Resource group env var is not present"); return new ResourceIdentifier(resourceId); } + + public string GetInstanceName() + { + var instanceName = EnvironmentVariables.OneFuzz.InstanceName + ?? throw new System.Exception("Instance Name env var is not present"); + + return instanceName; + } + + public async Async.Task GetInstanceId() + { + var blob = await _containers.GetBlob("base-config", "instance_id", StorageType.Config); + if (blob == null) + { + throw new System.Exception("Blob Not Found"); + } + return System.Guid.Parse(System.Text.Encoding.Default.GetString(blob)); + } } diff --git a/src/ApiService/ApiService/onefuzzlib/Storage.cs b/src/ApiService/ApiService/onefuzzlib/Storage.cs index ffafb282bd..8269eb5eff 100644 --- a/src/ApiService/ApiService/onefuzzlib/Storage.cs +++ b/src/ApiService/ApiService/onefuzzlib/Storage.cs @@ -21,6 +21,7 @@ public interface IStorage public IEnumerable CorpusAccounts(); string GetPrimaryAccount(StorageType storageType); public (string?, string?) GetStorageAccountNameAndKey(string accountId); + public List GetAccounts(StorageType storageType); } public class Storage : IStorage @@ -114,4 +115,40 @@ public string GetPrimaryAccount(StorageType storageType) var key = storageAccount.GetKeys().Value.Keys.FirstOrDefault(); return (resourceId.Name, key?.Value); } + + public string ChooseAccounts(StorageType storageType) + { + var accounts = GetAccounts(storageType); + if (!accounts.Any()) + { + throw new Exception($"No Storage Accounts for {storageType}"); + } + + if (accounts.Count == 1) + { + return accounts[0]; + } + + // Use a random secondary storage account if any are available. This + // reduces IOP contention for the Storage Queues, which are only available + // on primary accounts + // + // security note: this is not used as a security feature + var random = new Random(); + var index = random.Next(accounts.Count); + + return accounts[index]; // nosec + } + public List GetAccounts(StorageType storageType) + { + if (storageType == StorageType.Corpus) + { + return CorpusAccounts().ToList(); + } else if(storageType == StorageType.Config) + { + return new List { GetFuncStorage() }; + } else { + throw new NotImplementedException(); + } + } } diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs new file mode 100644 index 0000000000..9a86d949c9 --- /dev/null +++ b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs @@ -0,0 +1,151 @@ +using ApiService.OneFuzzLib.Orm; +using Microsoft.OneFuzz.Service; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Linq; + +namespace Microsoft.OneFuzz.Service; + + +public interface IWebhookMessageLogOperations : IOrm +{ + IAsyncEnumerable SearchExpired(); +} + + +public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations +{ + const int EXPIRE_DAYS = 7; + + record WebhookMessageQueueObj( + Guid WebhookId, + Guid EventId + ); + + private readonly IQueue _queue; + private readonly ILogTracer _log; + private readonly IWebhookOperations _webhook; + + public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IWebhookOperations webhook) : base(storage) + { + _queue = queue; + _log = log; + _webhook = webhook; + } + + + public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) + { + var obj = new WebhookMessageQueueObj(webhookLog.WebhookId, webhookLog.EventId); + + TimeSpan? visibilityTimeout = webhookLog.State switch + { + WebhookMessageState.Queued => TimeSpan.Zero, + WebhookMessageState.Retrying => TimeSpan.FromSeconds(30), + _ => null + }; + + if (visibilityTimeout == null) + { + _log.WithTags( + new[] { + ("WebhookId", webhookLog.WebhookId.ToString()), + ("EventId", webhookLog.EventId.ToString()) } + ). + Error($"invalid WebhookMessage queue state, not queuing. {webhookLog.WebhookId}:{webhookLog.EventId} - {webhookLog.State}"); + } + else + { + await _queue.QueueObject("webhooks", obj, StorageType.Config, visibilityTimeout: visibilityTimeout); + } + } + + public async Task ProcessFromQueue(WebhookMessageQueueObj obj) + { + var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); + + if (message == null) + { + _log.WithTags( + new[] { + ("WebhookId", obj.WebhookId.ToString()), + ("EventId", obj.EventId.ToString()) } + ). + Error($"webhook message log not found for webhookId: {obj.WebhookId} and eventId: {obj.EventId}"); + return null; + } + + Process(message); + } + + private void Process(WebhookMessageLog message) + { + + if (message.State == WebhookMessageState.Failed || message.State == WebhookMessageState.Succeeded) + { + _log.WithTags( + new[] { + ("WebhookId", message.WebhookId.ToString()), + ("EventId", message.EventId.ToString()) } + ). + Error($"webhook message already handled. {message.WebhookId}:{message.EventId}"); + } + + // message.TryCount++; + var newMessage = message with { TryCount = message.TryCount + 1 }; + + _log.Info($"sending webhook: {message.WebhookId}:{message.EventId}"); + + } + + private async Task Send(WebhookMessageLog message) + { + var webhook = await _webhook.GetByWebhookId(message.WebhookId); + if (webhook == null) + { + _log.WithTags( + new[] { + ("WebhookId", message.WebhookId.ToString()), + } + ). + Error($"webhook not found for webhookId: {message.WebhookId}"); + return false; + } + + try + { + return _webhook.Send(message); + } + catch (Exception) + { + _log.WithTags( + new[] { + ("WebhookId", message.WebhookId.ToString()) + } + ). + Error($"webhook send failed. {message.WebhookId}"); + } + + } + + private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) + { + throw new NotImplementedException(); + } + + public IAsyncEnumerable SearchExpired() + { + var expireTime = (DateTimeOffset.UtcNow - TimeSpan.FromDays(EXPIRE_DAYS)).ToString("o"); + + var timeFilter = $"Timestamp lt datetime'{expireTime}'"; + return QueryAsync(filter: timeFilter); + } + + public async Async.Task GetWebhookMessageById(Guid webhookId, Guid eventId) + { + var data = QueryAsync(filter: $"PartitionKey eq '{webhookId}' and Rowkey eq '{eventId}'"); + + return await data.FirstOrDefaultAsync(); + } +} diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookMessageOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookMessageOperations.cs deleted file mode 100644 index 5dae4022cc..0000000000 --- a/src/ApiService/ApiService/onefuzzlib/WebhookMessageOperations.cs +++ /dev/null @@ -1,152 +0,0 @@ -using ApiService.OneFuzzLib.Orm; -using Microsoft.OneFuzz.Service; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace ApiService.OneFuzzLib; - - -public interface IWebhookMessageLogOperations : IOrm -{ - -} - - -public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations -{ - record WebhookMessageQueueObj( - Guid WebhookId, - Guid EventId - ); - - private readonly IQueue _queue; - private readonly ILogTracerFactory _loggerFactory; - public WebhookMessageLogOperations(IStorage storage, IQueue queue, IWebhookMessageLogOperations webhookMessage, ILogTracerFactory loggerFactory) : base(storage) - { - _queue = queue; - _webhookMessage = webhookMessage; - _loggerFactory = loggerFactory; - } - - - public async Task QueueWebhook(WebhookMessageLog webhookLog) - { - var log = _loggerFactory.MakeLogTracer(Guid.NewGuid()); - var obj = new WebhookMessageQueueObj(webhookLog.WebhookId, webhookLog.EventId); - - TimeSpan? visibilityTimeout = webhookLog.State switch - { - WebhookMessageState.Queued => TimeSpan.Zero, - WebhookMessageState.Retrying => TimeSpan.FromSeconds(30), - _ => null - }; - - if (visibilityTimeout == null) - { - log.AddTags( - new[] { - ("WebhookId", webhookLog.WebhookId.ToString()), - ("EventId", webhookLog.EventId.ToString()) } - ). - Error($"invalid WebhookMessage queue state, not queuing. {webhookLog.WebhookId}:{webhookLog.EventId} - {webhookLog.State}"); - } - else - { - await _queue.QueueObject("webhooks", obj, StorageType.Config, visibilityTimeout: visibilityTimeout); - } - } - - - public void process() - { - if (_webhookMessage.State == WebhookMessageState.Failed || _webhookMessage.State == WebhookMessageState.Succeeded) - { - log.AddTags( - new[] { - ("WebhookId", _webhookMessage.WebhookId.ToString()), - ("EventId", _webhookMessage.EventId.ToString()) } - ). - Error($"webhook message already handled. {_webhookMessage.WebhookId}:{_webhookMessage.EventId}"); - } - - _webhookMessage.TryCount++; - - log.info($"sending webhook: {_webhookMessage.WebhookId}:{_webhookMessage.EventId}") - - } - - - public async Task ProcessFromQueue(WebhookMessageQueueObj webhookMessage) - { - message = await _webhookMessage.get(webhookMessage.WebhookId, webhookMessage.EventId); - - if (message == null) - { - log.AddTags( - new[] { - ("WebhookId", webhookMessage.WebhookId.ToString()), - ("EventId", webhookMessage.EventId.ToString()) } - ). - Error($"Webhook with webhookId: {webhookMessage.WebhookId} and eventId: {webhookMessage.EventId} not found."); - ) - } - - message.process(); - } - - private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) - { - throw new NotImplementedException(); - } -} - - -public interface IWebhookOperations -{ - Task SendEvent(EventMessage eventMessage); -} - -public class WebhookOperations : Orm, IWebhookOperations -{ - private readonly IWebhookMessageLogOperations _webhookMessageLogOperations; - public WebhookOperations(IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations) - : base(storage) - { - _webhookMessageLogOperations = webhookMessageLogOperations; - } - - async public Task SendEvent(EventMessage eventMessage) - { - await foreach (var webhook in GetWebhooksCached()) - { - if (!webhook.EventTypes.Contains(eventMessage.EventType)) - { - continue; - } - await AddEvent(webhook, eventMessage); - } - } - - async private Task AddEvent(Webhook webhook, EventMessage eventMessage) - { - var message = new WebhookMessageLog( - EventId: eventMessage.EventId, - EventType: eventMessage.EventType, - Event: eventMessage.Event, - InstanceId: eventMessage.InstanceId, - InstanceName: eventMessage.InstanceName, - WebhookId: webhook.WebhookId - ); - - await _webhookMessageLogOperations.Replace(message); - } - - - //todo: caching - public IAsyncEnumerable GetWebhooksCached() - { - return QueryAsync(); - } - -} diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index b95e710955..c41d8db010 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -1,84 +1,22 @@ using ApiService.OneFuzzLib.Orm; using System; using System.Collections.Generic; +using System.Linq; namespace Microsoft.OneFuzz.Service; - -public interface IWebhookMessageLogOperations : IOrm -{ - IAsyncEnumerable SearchExpired(); -} - - -public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations -{ - const int EXPIRE_DAYS = 7; - - record WebhookMessageQueueObj( - Guid WebhookId, - Guid EventId - ); - - private readonly IQueue _queue; - private readonly ILogTracer _log; - public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log) : base(storage) - { - _queue = queue; - _log = log; - } - - - public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) - { - var obj = new WebhookMessageQueueObj(webhookLog.WebhookId, webhookLog.EventId); - - TimeSpan? visibilityTimeout = webhookLog.State switch - { - WebhookMessageState.Queued => TimeSpan.Zero, - WebhookMessageState.Retrying => TimeSpan.FromSeconds(30), - _ => null - }; - - if (visibilityTimeout == null) - { - _log.WithTags( - new[] { - ("WebhookId", webhookLog.WebhookId.ToString()), - ("EventId", webhookLog.EventId.ToString()) } - ). - Error($"invalid WebhookMessage queue state, not queuing. {webhookLog.WebhookId}:{webhookLog.EventId} - {webhookLog.State}"); - } - else - { - await _queue.QueueObject("webhooks", obj, StorageType.Config, visibilityTimeout: visibilityTimeout); - } - } - - private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) - { - throw new NotImplementedException(); - } - - public IAsyncEnumerable SearchExpired() - { - var expireTime = (DateTimeOffset.UtcNow - TimeSpan.FromDays(EXPIRE_DAYS)).ToString("o"); - - var timeFilter = $"Timestamp lt datetime'{expireTime}'"; - return QueryAsync(filter: timeFilter); - } -} - - public interface IWebhookOperations { Async.Task SendEvent(EventMessage eventMessage); + Async.Task GetByWebhookId(Guid webhookId); + Async.Task Send(WebhookMessageLog messageLog); } public class WebhookOperations : Orm, IWebhookOperations { private readonly IWebhookMessageLogOperations _webhookMessageLogOperations; private readonly ILogTracer _log; + public WebhookOperations(IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, ILogTracer log) : base(storage) { @@ -117,6 +55,31 @@ async private Async.Task AddEvent(Webhook webhook, EventMessage eventMessage) } } + public async Async.Task Send(WebhookMessageLog messageLog) + { + var webhook = await GetByWebhookId(messageLog.WebhookId); + if (webhook == null) + { + throw new Exception($"Webhook with WebhookId: {messageLog.WebhookId} Not Found"); + } + + } + + // public Tuple BuildMessage(Guid webhookId, Guid eventId, EventType eventType, Event webhookEvent, String? SeretToken, WebhookMessageFormat? messageFormat) + // { + // if (messageFormat != null && messageFormat == WebhookMessageFormat.EventGrid) + // { + // var eventGridMessage = new WebhookMessageEventGrid(Id: eventId, data: webhookEvent, DataVersion: "1.0.0", Subject: ) + // // var decoded = [JsonSerializer.Serialize()] + // } + // } + + public async Async.Task GetByWebhookId(Guid webhookId) + { + var data = QueryAsync(filter: $"PartitionKey eq '{webhookId}'"); + + return await data.FirstOrDefaultAsync(); + } //todo: caching public IAsyncEnumerable GetWebhooksCached() diff --git a/src/ApiService/ApiService/packages.lock.json b/src/ApiService/ApiService/packages.lock.json index 885036b6ad..12e7e9ead9 100644 --- a/src/ApiService/ApiService/packages.lock.json +++ b/src/ApiService/ApiService/packages.lock.json @@ -1,1039 +1,1049 @@ -{ - "version": 1, - "dependencies": { - "net6.0": { - "Azure.Core": { - "type": "Direct", - "requested": "[1.24.0, )", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "System.Diagnostics.DiagnosticSource": "4.6.0", - "System.Memory.Data": "1.0.2", - "System.Numerics.Vectors": "4.5.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Azure.Data.Tables": { - "type": "Direct", - "requested": "[12.5.0, )", - "resolved": "12.5.0", - "contentHash": "XeIxPf+rF1NXkX3NJSB0ZTNgU233vyPXGmaFsR0lUVibtWP/lj+Qu1FcPxoslURcX0KC+UgTb226nqVdHjoweQ==", - "dependencies": { - "Azure.Core": "1.22.0", - "System.Text.Json": "4.7.2" - } - }, - "Azure.Identity": { - "type": "Direct", - "requested": "[1.6.0, )", - "resolved": "1.6.0", - "contentHash": "EycyMsb6rD2PK9P0SyibFfEhvWWttdrYhyPF4f41uzdB/44yQlV+2Wehxyg489Rj6gbPvSPgbKq0xsHJBhipZA==", - "dependencies": { - "Azure.Core": "1.24.0", - "Microsoft.Identity.Client": "4.39.0", - "Microsoft.Identity.Client.Extensions.Msal": "2.19.3", - "System.Memory": "4.5.4", - "System.Security.Cryptography.ProtectedData": "4.7.0", - "System.Text.Json": "4.7.2", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Azure.Messaging.EventGrid": { - "type": "Direct", - "requested": "[4.10.0, )", - "resolved": "4.10.0", - "contentHash": "X3dh3Cek/7wFPUrBJ2KbnkJteGjWvKBoSBmD/uQm8reMIavCFTKhnl95F937eLn/2cSsm5l3oPHtYPFtDerA7Q==", - "dependencies": { - "Azure.Core": "1.24.0", - "System.Memory.Data": "1.0.2", - "System.Text.Json": "4.7.2" - } - }, - "Azure.ResourceManager": { - "type": "Direct", - "requested": "[1.0.0, )", - "resolved": "1.0.0", - "contentHash": "UGaoiPcJ8a9Et030+F3zc2KhTssPAgPm7uXm4E9kyNI4jYYenUe6zj2J1bTimaTfcOZnn5scSjSYxKtZCzftcA==", - "dependencies": { - "Azure.Core": "1.24.0", - "System.Text.Json": "4.7.2" - } - }, - "Azure.ResourceManager.Compute": { - "type": "Direct", - "requested": "[1.0.0-beta.8, )", - "resolved": "1.0.0-beta.8", - "contentHash": "rYYjjmEdmcOa8O4UgO/bdJ/qQclNZjuHdalxRJ0AhUHCORcM1f1BbIKR9CoN83IpfuEE+X+n5XY9QZcKvfrGVA==", - "dependencies": { - "Azure.Core": "1.24.0", - "Azure.ResourceManager": "1.0.0", - "System.Text.Json": "4.7.2" - } - }, - "Azure.ResourceManager.Network": { - "type": "Direct", - "requested": "[1.0.0-beta.7, )", - "resolved": "1.0.0-beta.7", - "contentHash": "Ih8tb6OwxEEEEXATVzgX6oHJzVr9p4X6GfOfBnNAI3aIt9+G8blyQLltaCcJAGJ+dO1sBT/Nalgj/HinO+cBlw==", - "dependencies": { - "Azure.Core": "1.24.0", - "Azure.ResourceManager": "1.0.0", - "System.Text.Json": "4.7.2" - } - }, - "Azure.ResourceManager.Resources": { - "type": "Direct", - "requested": "[1.0.0, )", - "resolved": "1.0.0", - "contentHash": "oNQRWfh05v9BiY8DMQjV+++kVafR+3ry2FGfMKObovKNfAb4i5J6DQpv0CUIx4jeIZe0fnhxyXRRCe293YtMqw==", - "dependencies": { - "Azure.Core": "1.24.0", - "Azure.ResourceManager": "1.0.0", - "System.Text.Json": "4.7.2" - } - }, - "Azure.ResourceManager.Storage": { - "type": "Direct", - "requested": "[1.0.0-beta.8, )", - "resolved": "1.0.0-beta.8", - "contentHash": "tlVqStqG53lyGxr0dRq2KSkFdeC/+NQImWgsRXD9o5R4qBia4cx7zAGYBlnDeUxh1WldSZF5ZsBi2n5SAwxbxQ==", - "dependencies": { - "Azure.Core": "1.24.0", - "Azure.ResourceManager": "1.0.0", - "System.Text.Json": "4.7.2" - } - }, - "Azure.Storage.Queues": { - "type": "Direct", - "requested": "[12.9.0, )", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", - "dependencies": { - "Azure.Storage.Common": "12.10.0", - "System.Memory.Data": "1.0.2", - "System.Text.Json": "4.7.2" - } - }, - "Microsoft.Azure.Functions.Worker": { - "type": "Direct", - "requested": "[1.6.0, )", - "resolved": "1.6.0", - "contentHash": "Gzq2IPcMCym6wPpFayLbuvhrfr72OEInJJlKaIAqU9+HldVaTt54cm3hPe7kIok+QuWnwb/TtYtlmrkR0Nbhsg==", - "dependencies": { - "Azure.Core": "1.10.0", - "Microsoft.Azure.Functions.Worker.Core": "1.4.0", - "Microsoft.Azure.Functions.Worker.Grpc": "1.3.1", - "Microsoft.Extensions.Hosting": "5.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "5.0.0" - } - }, - "Microsoft.Azure.Functions.Worker.Extensions.EventGrid": { - "type": "Direct", - "requested": "[2.1.0, )", - "resolved": "2.1.0", - "contentHash": "8Kjhxaj2gK2Bi5K5jiNAG/e9tTlRItFNCINj+kfUDMBbf5lsiZUBChyAQCxrnITeHKkwAtgXB7GBX4W1Xcoc0A==", - "dependencies": { - "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.0.0" - } - }, - "Microsoft.Azure.Functions.Worker.Extensions.Http": { - "type": "Direct", - "requested": "[3.0.13, )", - "resolved": "3.0.13", - "contentHash": "GX41psGbjLSPKuFnBcGGB7PAAdhfLsgxvGVsyGq/jQwgGwjAVRRx2UbSl35+imKwCPZdT5vGjq6YV1rgXIeEvA==", - "dependencies": { - "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.0.0" - } - }, - "Microsoft.Azure.Functions.Worker.Extensions.SignalRService": { - "type": "Direct", - "requested": "[1.7.0, )", - "resolved": "1.7.0", - "contentHash": "mgk7ZnrXLPCI70cYgqxi+TJMJJgRMPYzZwIFMpxP2cto3D6XSxbF8eGj46T4DwopBBqWpfJ4Y2QFB93hNb4Yxg==", - "dependencies": { - "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.1.0", - "Microsoft.Extensions.Primitives": "5.0.1", - "System.Text.Json": "5.0.2" - } - }, - "Microsoft.Azure.Functions.Worker.Extensions.Storage": { - "type": "Direct", - "requested": "[5.0.0, )", - "resolved": "5.0.0", - "contentHash": "VwcmWk29//8CkXCxCR7vxMnqsuh+O0018eavRCGscI+uRKHdvlHDG97vwfdwuTzwKuoo7ztQbAvHfkp+sxoiEQ==", - "dependencies": { - "Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs": "5.0.0", - "Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues": "5.0.0" - } - }, - "Microsoft.Azure.Functions.Worker.Extensions.Timer": { - "type": "Direct", - "requested": "[4.1.0, )", - "resolved": "4.1.0", - "contentHash": "8HvZaChaw40EKBfBew0XG132YhO6bEw0nznvey7gkhm9thUe6wkA2LXTXHXxcYefbx0rlh57WedSiJgKTG7MvQ==", - "dependencies": { - "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.0.0" - } - }, - "Microsoft.Azure.Functions.Worker.Sdk": { - "type": "Direct", - "requested": "[1.3.0, )", - "resolved": "1.3.0", - "contentHash": "g9oXOl9xr1O3alWItAiYLNu3BnXebLW51BRB06yuO86LPGRZewyJu88EwUdC2NU9wnIeE3/ObMuEAnRALZeuTQ==", - "dependencies": { - "Microsoft.Azure.Functions.Worker.Sdk.Analyzers": "1.1.0" - } - }, - "Microsoft.Extensions.Logging.ApplicationInsights": { - "type": "Direct", - "requested": "[2.20.0, )", - "resolved": "2.20.0", - "contentHash": "phuNUDeTlffkJi6zAsMQNOpijNOQ4Olda1WL2L+F33u4fqXmY+EGQnPg81rHW6dOXIYCQvrQUr2gVN5NNMvwKA==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.20.0", - "Microsoft.Extensions.Logging": "2.1.1" - } - }, - "Microsoft.Graph": { - "type": "Direct", - "requested": "[4.24.0, )", - "resolved": "4.24.0", - "contentHash": "OPyHQ+EzuYjp3XExGB0SvySXY3pxU+bXLl3ADdvje/yOMFvpNOpEu111tmh2aM/RCplaoMQjBA5oa9pUVIH0cg==", - "dependencies": { - "Microsoft.Graph.Core": "2.0.8" - } - }, - "Microsoft.Identity.Client": { - "type": "Direct", - "requested": "[4.43.0, )", - "resolved": "4.43.0", - "contentHash": "uaUMZB3Ywi7IPVvgRZOQotlYhD8sA4wtZESkA0qF9SYAifr1RzJyaGTFtfbAyZ/J5kGUhRklrCJIRpd0MaihKQ==" - }, - "Microsoft.Identity.Web.TokenCache": { - "type": "Direct", - "requested": "[1.23.1, )", - "resolved": "1.23.1", - "contentHash": "fU85i6XDUXL/z6B+pTmNZbof0hL9Jkgsi6GWpQEWjL7Ek0GH0A8btxbqzojPCRdGN7EK/vyEVu5Smy9/spZj2g==", - "dependencies": { - "Microsoft.AspNetCore.DataProtection": "5.0.8", - "Microsoft.Extensions.Caching.Memory": "5.0.0", - "Microsoft.Extensions.Logging": "5.0.0", - "Microsoft.Identity.Client": "4.42.0", - "System.Text.Encodings.Web": "5.0.1" - } - }, - "System.IdentityModel.Tokens.Jwt": { - "type": "Direct", - "requested": "[6.17.0, )", - "resolved": "6.17.0", - "contentHash": "G3rY4WLr54Mo+97+AEq0ANpiKvW7E8Qu5bKWfVMa7rkyJtvrOxUqp/OLqrGw/6JDbD5GlxnAtFKukGteUuB0rQ==", - "dependencies": { - "Microsoft.IdentityModel.JsonWebTokens": "6.17.0", - "Microsoft.IdentityModel.Tokens": "6.17.0" - } - }, - "System.Linq.Async": { - "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } - }, - "Azure.Storage.Common": { - "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", - "dependencies": { - "Azure.Core": "1.22.0", - "System.IO.Hashing": "6.0.0" - } - }, - "Google.Protobuf": { - "type": "Transitive", - "resolved": "3.15.8", - "contentHash": "tA0S9QXJq+r3CjwBlcn5glEUrbdAxhPWO4yhq5+ycn6WW6+nsvqzO6Qf6NE9XWbEz/F2QSpBTxjdTI7SvVy7CQ==", - "dependencies": { - "System.Memory": "4.5.3", - "System.Runtime.CompilerServices.Unsafe": "4.5.2" - } - }, - "Grpc.Core.Api": { - "type": "Transitive", - "resolved": "2.37.0", - "contentHash": "ubqW2nTpiHyDudYGVXM+Vjh6WbgsI1fVQxsDK14/GnyPgiNMuNl8+GQcAYp5QPhAk5H4fjHJPI+KvbpVk8z6iQ==", - "dependencies": { - "System.Memory": "4.5.3" - } - }, - "Grpc.Net.Client": { - "type": "Transitive", - "resolved": "2.37.0", - "contentHash": "oMDNXAPkBzfXljv/ZzlZSf22TEstBNI6je85/c3iztlFbbixTMLgi0sIu/uHtEKoEWUPr0nmBMvD+jtqKorGTg==", - "dependencies": { - "Grpc.Net.Common": "2.37.0", - "Microsoft.Extensions.Logging.Abstractions": "3.0.3" - } - }, - "Grpc.Net.ClientFactory": { - "type": "Transitive", - "resolved": "2.37.0", - "contentHash": "zyeFej1A36+s5K6+zDUirmDEGHEFnHapQisT7YsR9nQqKsw1uYqjtG1gSVSg/Zvk0KYeLHs5/URtTU71kS4APg==", - "dependencies": { - "Grpc.Net.Client": "2.37.0", - "Microsoft.Extensions.Http": "3.0.3" - } - }, - "Grpc.Net.Common": { - "type": "Transitive", - "resolved": "2.37.0", - "contentHash": "V7fZb+87qB6Jio6uWXDHkxI9WT+y4EFwicAHkzB2lm/9wJazD0V35HhQjxvoONsldObaUimjqd4b/XZ0G07sDQ==", - "dependencies": { - "Grpc.Core.Api": "2.37.0" - } - }, - "Microsoft.ApplicationInsights": { - "type": "Transitive", - "resolved": "2.20.0", - "contentHash": "mb+EC5j06Msn5HhKrhrsMAst6JxvYUnphQMGY2cixCabgGAO3q79Y8o/p1Zce1Azgd1IVkRKAMzAV4vDCbXOqA==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "5.0.0" - } - }, - "Microsoft.AspNetCore.Cryptography.Internal": { - "type": "Transitive", - "resolved": "5.0.8", - "contentHash": "giHheyNLOb+cAHpb8b0GhaS0xJ+hAIIDSyWPe5aOPwpgctsjOPRKFyn/268xv+zBVuEtyRJJEnBUlkOVzyIpZA==" - }, - "Microsoft.AspNetCore.DataProtection": { - "type": "Transitive", - "resolved": "5.0.8", - "contentHash": "wCMdfuKA+ePcB4nEDau5tNhhhC5NFa2LEXoRhk2Xaot13FFlyKA4t5UzIyV/OnAfB/bqbAIvChJD+biWY7u5SA==", - "dependencies": { - "Microsoft.AspNetCore.Cryptography.Internal": "5.0.8", - "Microsoft.AspNetCore.DataProtection.Abstractions": "5.0.8", - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "5.0.0", - "Microsoft.Extensions.Options": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.Security.Cryptography.Xml": "5.0.0" - } - }, - "Microsoft.AspNetCore.DataProtection.Abstractions": { - "type": "Transitive", - "resolved": "5.0.8", - "contentHash": "ZI9S2NGjuOKXN3PxJcF8EKVwd1cqpWyUSqiVoH8gqq5tlHaXULwPmoR0DBOFON4sEFETRWI69f5RQ3tJWw205A==" - }, - "Microsoft.Azure.Functions.Worker.Core": { - "type": "Transitive", - "resolved": "1.4.0", - "contentHash": "6fTSb6JDm+1CNKsaPziL36c3tfN4xxYnC9XoJsm0g9tY+72dVqUa2aPc6RtkwBmT5sjNrsUDlUC+IhG+ehjppQ==", - "dependencies": { - "Azure.Core": "1.10.0", - "Microsoft.Extensions.Hosting": "5.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "5.0.0" - } - }, - "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kAs9BTuzdOvyuN2m5CYyQzyvzXKJ6hhIOgcwm0W8Q+Fwj91a1eBmRSi9pVzpM4V3skNt/+pkPD3wxFD4nEw0bg==" - }, - "Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Cr+ziBQA/Lyt5rMURHBje+foGook+B82gnAfEE32bHcXGlpKJnnVdLqHy0OqHliUksFddkRYS2gY8dYx+NH/mA==", - "dependencies": { - "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.1.0" - } - }, - "Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "cF95kiiU6PD9sptrV3GKQKzRv2DYATYNTpOtvUtbAYQ4xPFKgF4ke3fDBcu+cu2O1/C8FQ7MhzkEQv00bx552A==", - "dependencies": { - "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.1.0" - } - }, - "Microsoft.Azure.Functions.Worker.Grpc": { - "type": "Transitive", - "resolved": "1.3.1", - "contentHash": "lMlbyfRagSQZVWN73jnaB0tVTMhfKHSy6IvFXC7fCGh+7uA9LJNjcMOQbVkUnmvb/I/SxslMqD7xcebrxFL3TQ==", - "dependencies": { - "Azure.Core": "1.10.0", - "Google.Protobuf": "3.15.8", - "Grpc.Net.Client": "2.37.0", - "Grpc.Net.ClientFactory": "2.37.0", - "Microsoft.Azure.Functions.Worker.Core": "1.3.1", - "Microsoft.Extensions.Hosting": "5.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "5.0.0" - } - }, - "Microsoft.Azure.Functions.Worker.Sdk.Analyzers": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "J7AZ9iv/UCd4Di0c84h1P/Sa1aQr5uqO0EBUKwE0AZeWJ11dDfKAwxMiAxYOKR+giy31DWBnuFc4GKY3BQYUjg==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ==" - }, - "Microsoft.Extensions.Caching.Abstractions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "bu8As90/SBAouMZ6fJ+qRNo1X+KgHGrVueFhhYi+E5WqEhcnp2HoWRFnMzXQ6g4RdZbvPowFerSbKNH4Dtg5yg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "5.0.0" - } - }, - "Microsoft.Extensions.Caching.Memory": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "/1qPCleFOkJe0O+xmFqCNLFYQZTJz965sVw8CUB/BQgsApBwzAUsL2BUkDvQW+geRUVTXUS9zLa0pBjC2VJ1gA==", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "5.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "5.0.0", - "Microsoft.Extensions.Options": "5.0.0", - "Microsoft.Extensions.Primitives": "5.0.0" - } - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "LN322qEKHjuVEhhXueTUe7RNePooZmS8aGid5aK2woX3NPjSnONFyKUc6+JknOS6ce6h2tCLfKPTBXE3mN/6Ag==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", - "Microsoft.Extensions.Primitives": "5.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "ETjSBHMp3OAZ4HxGQYpwyGsD8Sw5FegQXphi0rpoGMT74S4+I2mm7XJEswwn59XAaKOzC15oDSOWEE8SzDCd6Q==", - "dependencies": { - "Microsoft.Extensions.Primitives": "5.0.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Of1Irt1+NzWO+yEYkuDh5TpT4On7LKl98Q9iLqCdOZps6XXEWDj3AKtmyvzJPVXZe4apmkJJIiDL7rR1yC+hjQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0" - } - }, - "Microsoft.Extensions.Configuration.CommandLine": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "OelM+VQdhZ0XMXsEQBq/bt3kFzD+EBGqR4TAgFDRAye0JfvHAaRi+3BxCRcwqUAwDhV0U0HieljBGHlTgYseRA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "5.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0" - } - }, - "Microsoft.Extensions.Configuration.EnvironmentVariables": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "fqh6y6hAi0Z0fRsb4B/mP9OkKkSlifh5osa+N/YSQ+/S2a//+zYApZMUC1XeP9fdjlgZoPQoZ72Q2eLHyKLddQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "5.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0" - } - }, - "Microsoft.Extensions.Configuration.FileExtensions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "rRdspYKA18ViPOISwAihhCMbusHsARCOtDMwa23f+BGEdIjpKPlhs3LLjmKlxfhpGXBjIsS0JpXcChjRUN+PAw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "5.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0", - "Microsoft.Extensions.FileProviders.Physical": "5.0.0", - "Microsoft.Extensions.Primitives": "5.0.0" - } - }, - "Microsoft.Extensions.Configuration.Json": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Pak8ymSUfdzPfBTLHxeOwcR32YDbuVfhnH2hkfOLnJNQd19ItlBdpMjIDY9C5O/nS2Sn9bzDMai0ZrvF7KyY/Q==", - "dependencies": { - "Microsoft.Extensions.Configuration": "5.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "5.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0" - } - }, - "Microsoft.Extensions.Configuration.UserSecrets": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "+tK3seG68106lN277YWQvqmfyI/89w0uTu/5Gz5VYSUu5TI4mqwsaWLlSmT9Bl1yW/i1Nr06gHJxqaqB5NU9Tw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", - "Microsoft.Extensions.Configuration.Json": "5.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0", - "Microsoft.Extensions.FileProviders.Physical": "5.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Rc2kb/p3Ze6cP6rhFC3PJRdWGbLvSHZc0ev7YlyeU6FmHciDMLrhoVoTUEzKPhN5ZjFgKF1Cf5fOz8mCMIkvpA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==" - }, - "Microsoft.Extensions.FileProviders.Abstractions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "iuZIiZ3mteEb+nsUqpGXKx2cGF+cv6gWPd5jqQI4hzqdiJ6I94ddLjKhQOuRW1lueHwocIw30xbSHGhQj0zjdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "5.0.0" - } - }, - "Microsoft.Extensions.FileProviders.Physical": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "1rkd8UO2qf21biwO7X0hL9uHP7vtfmdv/NLvKgCRHkdz1XnW8zVQJXyEYiN68WYpExgtVWn55QF0qBzgfh1mGg==", - "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0", - "Microsoft.Extensions.FileSystemGlobbing": "5.0.0", - "Microsoft.Extensions.Primitives": "5.0.0" - } - }, - "Microsoft.Extensions.FileSystemGlobbing": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "ArliS8lGk8sWRtrWpqI8yUVYJpRruPjCDT+EIjrgkA/AAPRctlAkRISVZ334chAKktTLzD1+PK8F5IZpGedSqA==" - }, - "Microsoft.Extensions.Hosting": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "hiokSU1TOVfcqpQAnpiOzP2rE9p+niq92g5yeAnwlbSrUlIdIS6M8emCknZvhdOagQA9x5YWNwe1n0kFUwE0NQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "5.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", - "Microsoft.Extensions.Configuration.Binder": "5.0.0", - "Microsoft.Extensions.Configuration.CommandLine": "5.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "5.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "5.0.0", - "Microsoft.Extensions.Configuration.Json": "5.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "5.0.0", - "Microsoft.Extensions.DependencyInjection": "5.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0", - "Microsoft.Extensions.FileProviders.Physical": "5.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging.Configuration": "5.0.0", - "Microsoft.Extensions.Logging.Console": "5.0.0", - "Microsoft.Extensions.Logging.Debug": "5.0.0", - "Microsoft.Extensions.Logging.EventLog": "5.0.0", - "Microsoft.Extensions.Logging.EventSource": "5.0.0", - "Microsoft.Extensions.Options": "5.0.0" - } - }, - "Microsoft.Extensions.Hosting.Abstractions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "cbUOCePYBl1UhM+N2zmDSUyJ6cODulbtUd9gEzMFIK3RQDtP/gJsE08oLcBSXH3Q1RAQ0ex7OAB3HeTKB9bXpg==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0" - } - }, - "Microsoft.Extensions.Http": { - "type": "Transitive", - "resolved": "3.0.3", - "contentHash": "dcyB8szIcSynjVZRuFgqkZpPgTc5zeRSj1HMXSmNqWbHYKiPYJl8ZQgBHz6wmZNSUUNGpCs5uxUg8DZHHDC1Ew==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "3.0.3", - "Microsoft.Extensions.Logging": "3.0.3", - "Microsoft.Extensions.Options": "3.0.3" - } - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "MgOwK6tPzB6YNH21wssJcw/2MKwee8b2gI7SllYfn6rvTpIrVvVS5HAjSU2vqSku1fwqRvWP0MdIi14qjd93Aw==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection": "5.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "5.0.0", - "Microsoft.Extensions.Options": "5.0.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "NxP6ahFcBnnSfwNBi2KH2Oz8Xl5Sm2krjId/jRR3I7teFphwiUoUeZPwTNA21EX+5PtjqmyAvKaOeBXcJjcH/w==" - }, - "Microsoft.Extensions.Logging.Configuration": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "N3/d0HeMRnBekadbZlmbp+In8EvNNkQHSdbtRzjrGVckdZWpYs5GNrAfaYqVplDFW0WUedSaFJ3khB50BWYGsw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "5.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", - "Microsoft.Extensions.Configuration.Binder": "5.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "5.0.0", - "Microsoft.Extensions.Options": "5.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "5.0.0" - } - }, - "Microsoft.Extensions.Logging.Console": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "jH0wbWhfvXjOVmCkbra4vbiovDtTUIWLQjCeJ7Xun3h4AHvwfzm7V7wlsXKs3tNnPrsCxZ9oaV0vUAgGY1JxOA==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging.Configuration": "5.0.0", - "Microsoft.Extensions.Options": "5.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "5.0.0" - } - }, - "Microsoft.Extensions.Logging.Debug": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "9dvt0xqRrClvhaPNpfyS39WxnW9G55l5lrV5ZX7IrEgwo4VwtmJKtoPiKVYKbhAuOBGUI5WY3hWLvF+PSbJp5A==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "5.0.0" - } - }, - "Microsoft.Extensions.Logging.EventLog": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "CYzsgF2lqgahGl/HuErsIDaZZ9ueN+MBjGfO/0jVDLPaXLaywxlGKFpDgXMaB053DRYZwD1H2Lb1I60mTXS3jg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "5.0.0", - "Microsoft.Extensions.Options": "5.0.0", - "System.Diagnostics.EventLog": "5.0.0" - } - }, - "Microsoft.Extensions.Logging.EventSource": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "hF+D6PJkrM0qXcSEGs1BwZwgP8c0BRkj26P/5wmYTcHKOp52GRey/Z/YKRmRIHIrXxj9tz/JgIjU9oWmiJ5HMw==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.Logging": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "5.0.0", - "Microsoft.Extensions.Options": "5.0.0", - "Microsoft.Extensions.Primitives": "5.0.0" - } - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "CBvR92TCJ5uBIdd9/HzDSrxYak+0W/3+yxrNg8Qm6Bmrkh5L+nu6m3WeazQehcZ5q1/6dDA7J5YdQjim0165zg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.Primitives": "5.0.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "280RxNJqOeQqq47aJLy5D9LN61CAWeuRA83gPToQ8B9jl9SNdQ5EXjlfvF66zQI5AXMl+C/3hGnbtIEN+X3mqA==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", - "Microsoft.Extensions.Configuration.Binder": "5.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", - "Microsoft.Extensions.Options": "5.0.0", - "Microsoft.Extensions.Primitives": "5.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "5.0.1", - "contentHash": "5WPSmL4YeP7eW+Vc8XZ4DwjYWBAiSwDV9Hm63JJWcz1Ie3Xjv4KuJXzgCstj48LkLfVCYa7mLcx7y+q6yqVvtw==" - }, - "Microsoft.Graph.Core": { - "type": "Transitive", - "resolved": "2.0.8", - "contentHash": "CcsCgY1O+LQaKMJkQCpVZPmK6uiFFUu49MwqTxoTBYtFG2/NwyGAIpla5gct8jWlS0DBKXWAyRlIzKt/6UGIDQ==", - "dependencies": { - "Azure.Core": "1.22.0", - "Microsoft.Identity.Client": "4.41.0", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.15.1", - "System.Diagnostics.DiagnosticSource": "4.7.1", - "System.Security.Claims": "4.3.0", - "System.Text.Json": "6.0.2" - } - }, - "Microsoft.Identity.Client.Extensions.Msal": { - "type": "Transitive", - "resolved": "2.19.3", - "contentHash": "zVVZjn8aW7W79rC1crioDgdOwaFTQorsSO6RgVlDDjc7MvbEGz071wSNrjVhzR0CdQn6Sefx7Abf1o7vasmrLg==", - "dependencies": { - "Microsoft.Identity.Client": "4.38.0", - "System.Security.Cryptography.ProtectedData": "4.5.0" - } - }, - "Microsoft.IdentityModel.JsonWebTokens": { - "type": "Transitive", - "resolved": "6.17.0", - "contentHash": "I3cSVE185qF3a222/iQIdmBFhrhZBtz7wZ1RUUbMuHC1un79XCI7vggbWdmbqIttFcUoeziemadO6t+3FLjcSA==", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "6.17.0" - } - }, - "Microsoft.IdentityModel.Logging": { - "type": "Transitive", - "resolved": "6.17.0", - "contentHash": "Ix6/CMLDoo939NDf1ARDuGK6YERY7pAX9WYbfwb4gZqx7r52unMFIykJk+zlEBX7jjtbDz/0uzikQFvheV9KsQ==" - }, - "Microsoft.IdentityModel.Protocols": { - "type": "Transitive", - "resolved": "6.15.1", - "contentHash": "6nHr+4yE8vj620Vy4L0pl7kmkvWc06wBrJ+AOo/gjqzu/UD/MYgySUqRGlZYrvvNmKkUWMw4hdn78MPCb4bstA==", - "dependencies": { - "Microsoft.IdentityModel.Logging": "6.15.1", - "Microsoft.IdentityModel.Tokens": "6.15.1" - } - }, - "Microsoft.IdentityModel.Protocols.OpenIdConnect": { - "type": "Transitive", - "resolved": "6.15.1", - "contentHash": "WwecgT/PNrytLNUWjkYtnnG2LXMAzkINSaZM+8dPPiEpOGz1bQDBWAenTSurYICxGoA1sOPriFXk+ocnQyprKw==", - "dependencies": { - "Microsoft.IdentityModel.Protocols": "6.15.1", - "System.IdentityModel.Tokens.Jwt": "6.15.1" - } - }, - "Microsoft.IdentityModel.Tokens": { - "type": "Transitive", - "resolved": "6.17.0", - "contentHash": "mhOe+d9BQg5U45TkTCyXAFOjl7RvwaFj6v9qo8b+WFolkuGsfjSFfQ+WI9D3ho9sD/fK75gvL4JptmjLzyUPkw==", - "dependencies": { - "Microsoft.CSharp": "4.5.0", - "Microsoft.IdentityModel.Logging": "6.17.0", - "System.Security.Cryptography.Cng": "4.5.0" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0" - } - }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" - }, - "System.Diagnostics.EventLog": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "FHkCwUfsTs+/5tsK+c0egLfacUgbhvcwi3wUFWSEEArSXao343mYqcpOVVFMlcCkdNtjU4YwAWaKYwal6f02og==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "5.0.0" - } - }, - "System.Formats.Asn1": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w==" - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.Hashing": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Rfm2jYCaUeGysFEZjDe7j1R4x6Z6BzumS/vUT5a1AA/AWJuGX71PoGB0RmpyX3VmrGqVnAwtfMn39OHR8Y/5+g==" - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" - }, - "System.Memory.Data": { - "type": "Transitive", - "resolved": "1.0.2", - "contentHash": "JGkzeqgBsiZwKJZ1IxPNsDFZDhUvuEdX8L8BDC8N3KOj+6zMcNU28CNN59TpZE/VJYy9cP+5M+sbxtWJx3/xtw==", - "dependencies": { - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.6.0" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Security.Claims": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "P/+BR/2lnc4PNDHt/TPBAWHVMLMRHsyYZbU1NphW4HIWzCggz8mJbTQQ3MKljFE7LS3WagmVFuBgoLcFzYXlkA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Security.Principal": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==", - "dependencies": { - "System.Formats.Asn1": "5.0.0" - } - }, - "System.Security.Cryptography.Pkcs": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "9TPLGjBCGKmNvG8pjwPeuYy0SMVmGZRwlTZvyPHDbYv/DRkoeumJdfumaaDNQzVGMEmbWtg07zUpSW9q70IlDQ==", - "dependencies": { - "System.Formats.Asn1": "5.0.0", - "System.Security.Cryptography.Cng": "5.0.0" - } - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" - }, - "System.Security.Cryptography.Xml": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "MYmkHtCW+paFmPGFDktnLdOeH3zUrNchbZNki87E1ejNSMm9enSRbJokmvFrsWUrDE4bRE1lVeAle01+t6SGhA==", - "dependencies": { - "System.Security.Cryptography.Pkcs": "5.0.0", - "System.Security.Permissions": "5.0.0" - } - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Windows.Extensions": "5.0.0" - } - }, - "System.Security.Principal": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "0nE2gwXLn3PTBOPwORLqwuYvWB+Beomt9ZBX+6LmogMNKUvfD1SoDb/ycB1vBntT94rGaB/SvxEyeLu14H6aEg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==", - "dependencies": { - "System.Drawing.Common": "5.0.0" - } - } - } - } +{ + "version": 1, + "dependencies": { + "net6.0": { + "Azure.Core": { + "type": "Direct", + "requested": "[1.24.0, )", + "resolved": "1.24.0", + "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.1", + "System.Diagnostics.DiagnosticSource": "4.6.0", + "System.Memory.Data": "1.0.2", + "System.Numerics.Vectors": "4.5.0", + "System.Text.Encodings.Web": "4.7.2", + "System.Text.Json": "4.7.2", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Azure.Data.Tables": { + "type": "Direct", + "requested": "[12.5.0, )", + "resolved": "12.5.0", + "contentHash": "XeIxPf+rF1NXkX3NJSB0ZTNgU233vyPXGmaFsR0lUVibtWP/lj+Qu1FcPxoslURcX0KC+UgTb226nqVdHjoweQ==", + "dependencies": { + "Azure.Core": "1.22.0", + "System.Text.Json": "4.7.2" + } + }, + "Azure.Identity": { + "type": "Direct", + "requested": "[1.6.0, )", + "resolved": "1.6.0", + "contentHash": "EycyMsb6rD2PK9P0SyibFfEhvWWttdrYhyPF4f41uzdB/44yQlV+2Wehxyg489Rj6gbPvSPgbKq0xsHJBhipZA==", + "dependencies": { + "Azure.Core": "1.24.0", + "Microsoft.Identity.Client": "4.39.0", + "Microsoft.Identity.Client.Extensions.Msal": "2.19.3", + "System.Memory": "4.5.4", + "System.Security.Cryptography.ProtectedData": "4.7.0", + "System.Text.Json": "4.7.2", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Azure.Messaging.EventGrid": { + "type": "Direct", + "requested": "[4.10.0, )", + "resolved": "4.10.0", + "contentHash": "X3dh3Cek/7wFPUrBJ2KbnkJteGjWvKBoSBmD/uQm8reMIavCFTKhnl95F937eLn/2cSsm5l3oPHtYPFtDerA7Q==", + "dependencies": { + "Azure.Core": "1.24.0", + "System.Memory.Data": "1.0.2", + "System.Text.Json": "4.7.2" + } + }, + "Azure.ResourceManager": { + "type": "Direct", + "requested": "[1.0.0, )", + "resolved": "1.0.0", + "contentHash": "UGaoiPcJ8a9Et030+F3zc2KhTssPAgPm7uXm4E9kyNI4jYYenUe6zj2J1bTimaTfcOZnn5scSjSYxKtZCzftcA==", + "dependencies": { + "Azure.Core": "1.24.0", + "System.Text.Json": "4.7.2" + } + }, + "Azure.ResourceManager.Compute": { + "type": "Direct", + "requested": "[1.0.0-beta.8, )", + "resolved": "1.0.0-beta.8", + "contentHash": "rYYjjmEdmcOa8O4UgO/bdJ/qQclNZjuHdalxRJ0AhUHCORcM1f1BbIKR9CoN83IpfuEE+X+n5XY9QZcKvfrGVA==", + "dependencies": { + "Azure.Core": "1.24.0", + "Azure.ResourceManager": "1.0.0", + "System.Text.Json": "4.7.2" + } + }, + "Azure.ResourceManager.Network": { + "type": "Direct", + "requested": "[1.0.0-beta.7, )", + "resolved": "1.0.0-beta.7", + "contentHash": "Ih8tb6OwxEEEEXATVzgX6oHJzVr9p4X6GfOfBnNAI3aIt9+G8blyQLltaCcJAGJ+dO1sBT/Nalgj/HinO+cBlw==", + "dependencies": { + "Azure.Core": "1.24.0", + "Azure.ResourceManager": "1.0.0", + "System.Text.Json": "4.7.2" + } + }, + "Azure.ResourceManager.Resources": { + "type": "Direct", + "requested": "[1.0.0, )", + "resolved": "1.0.0", + "contentHash": "oNQRWfh05v9BiY8DMQjV+++kVafR+3ry2FGfMKObovKNfAb4i5J6DQpv0CUIx4jeIZe0fnhxyXRRCe293YtMqw==", + "dependencies": { + "Azure.Core": "1.24.0", + "Azure.ResourceManager": "1.0.0", + "System.Text.Json": "4.7.2" + } + }, + "Azure.ResourceManager.Storage": { + "type": "Direct", + "requested": "[1.0.0-beta.8, )", + "resolved": "1.0.0-beta.8", + "contentHash": "tlVqStqG53lyGxr0dRq2KSkFdeC/+NQImWgsRXD9o5R4qBia4cx7zAGYBlnDeUxh1WldSZF5ZsBi2n5SAwxbxQ==", + "dependencies": { + "Azure.Core": "1.24.0", + "Azure.ResourceManager": "1.0.0", + "System.Text.Json": "4.7.2" + } + }, + "Azure.Storage.Blobs": { + "type": "Direct", + "requested": "[12.11.0, )", + "resolved": "12.11.0", + "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "dependencies": { + "Azure.Storage.Common": "12.10.0", + "System.Text.Json": "4.7.2" + } + }, + "Azure.Storage.Queues": { + "type": "Direct", + "requested": "[12.9.0, )", + "resolved": "12.9.0", + "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "dependencies": { + "Azure.Storage.Common": "12.10.0", + "System.Memory.Data": "1.0.2", + "System.Text.Json": "4.7.2" + } + }, + "Microsoft.Azure.Functions.Worker": { + "type": "Direct", + "requested": "[1.6.0, )", + "resolved": "1.6.0", + "contentHash": "Gzq2IPcMCym6wPpFayLbuvhrfr72OEInJJlKaIAqU9+HldVaTt54cm3hPe7kIok+QuWnwb/TtYtlmrkR0Nbhsg==", + "dependencies": { + "Azure.Core": "1.10.0", + "Microsoft.Azure.Functions.Worker.Core": "1.4.0", + "Microsoft.Azure.Functions.Worker.Grpc": "1.3.1", + "Microsoft.Extensions.Hosting": "5.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "5.0.0" + } + }, + "Microsoft.Azure.Functions.Worker.Extensions.EventGrid": { + "type": "Direct", + "requested": "[2.1.0, )", + "resolved": "2.1.0", + "contentHash": "8Kjhxaj2gK2Bi5K5jiNAG/e9tTlRItFNCINj+kfUDMBbf5lsiZUBChyAQCxrnITeHKkwAtgXB7GBX4W1Xcoc0A==", + "dependencies": { + "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.0.0" + } + }, + "Microsoft.Azure.Functions.Worker.Extensions.Http": { + "type": "Direct", + "requested": "[3.0.13, )", + "resolved": "3.0.13", + "contentHash": "GX41psGbjLSPKuFnBcGGB7PAAdhfLsgxvGVsyGq/jQwgGwjAVRRx2UbSl35+imKwCPZdT5vGjq6YV1rgXIeEvA==", + "dependencies": { + "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.0.0" + } + }, + "Microsoft.Azure.Functions.Worker.Extensions.SignalRService": { + "type": "Direct", + "requested": "[1.7.0, )", + "resolved": "1.7.0", + "contentHash": "mgk7ZnrXLPCI70cYgqxi+TJMJJgRMPYzZwIFMpxP2cto3D6XSxbF8eGj46T4DwopBBqWpfJ4Y2QFB93hNb4Yxg==", + "dependencies": { + "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.1.0", + "Microsoft.Extensions.Primitives": "5.0.1", + "System.Text.Json": "5.0.2" + } + }, + "Microsoft.Azure.Functions.Worker.Extensions.Storage": { + "type": "Direct", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "VwcmWk29//8CkXCxCR7vxMnqsuh+O0018eavRCGscI+uRKHdvlHDG97vwfdwuTzwKuoo7ztQbAvHfkp+sxoiEQ==", + "dependencies": { + "Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs": "5.0.0", + "Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues": "5.0.0" + } + }, + "Microsoft.Azure.Functions.Worker.Extensions.Timer": { + "type": "Direct", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "8HvZaChaw40EKBfBew0XG132YhO6bEw0nznvey7gkhm9thUe6wkA2LXTXHXxcYefbx0rlh57WedSiJgKTG7MvQ==", + "dependencies": { + "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.0.0" + } + }, + "Microsoft.Azure.Functions.Worker.Sdk": { + "type": "Direct", + "requested": "[1.3.0, )", + "resolved": "1.3.0", + "contentHash": "g9oXOl9xr1O3alWItAiYLNu3BnXebLW51BRB06yuO86LPGRZewyJu88EwUdC2NU9wnIeE3/ObMuEAnRALZeuTQ==", + "dependencies": { + "Microsoft.Azure.Functions.Worker.Sdk.Analyzers": "1.1.0" + } + }, + "Microsoft.Extensions.Logging.ApplicationInsights": { + "type": "Direct", + "requested": "[2.20.0, )", + "resolved": "2.20.0", + "contentHash": "phuNUDeTlffkJi6zAsMQNOpijNOQ4Olda1WL2L+F33u4fqXmY+EGQnPg81rHW6dOXIYCQvrQUr2gVN5NNMvwKA==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.20.0", + "Microsoft.Extensions.Logging": "2.1.1" + } + }, + "Microsoft.Graph": { + "type": "Direct", + "requested": "[4.24.0, )", + "resolved": "4.24.0", + "contentHash": "OPyHQ+EzuYjp3XExGB0SvySXY3pxU+bXLl3ADdvje/yOMFvpNOpEu111tmh2aM/RCplaoMQjBA5oa9pUVIH0cg==", + "dependencies": { + "Microsoft.Graph.Core": "2.0.8" + } + }, + "Microsoft.Identity.Client": { + "type": "Direct", + "requested": "[4.43.0, )", + "resolved": "4.43.0", + "contentHash": "uaUMZB3Ywi7IPVvgRZOQotlYhD8sA4wtZESkA0qF9SYAifr1RzJyaGTFtfbAyZ/J5kGUhRklrCJIRpd0MaihKQ==" + }, + "Microsoft.Identity.Web.TokenCache": { + "type": "Direct", + "requested": "[1.23.1, )", + "resolved": "1.23.1", + "contentHash": "fU85i6XDUXL/z6B+pTmNZbof0hL9Jkgsi6GWpQEWjL7Ek0GH0A8btxbqzojPCRdGN7EK/vyEVu5Smy9/spZj2g==", + "dependencies": { + "Microsoft.AspNetCore.DataProtection": "5.0.8", + "Microsoft.Extensions.Caching.Memory": "5.0.0", + "Microsoft.Extensions.Logging": "5.0.0", + "Microsoft.Identity.Client": "4.42.0", + "System.Text.Encodings.Web": "5.0.1" + } + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Direct", + "requested": "[6.17.0, )", + "resolved": "6.17.0", + "contentHash": "G3rY4WLr54Mo+97+AEq0ANpiKvW7E8Qu5bKWfVMa7rkyJtvrOxUqp/OLqrGw/6JDbD5GlxnAtFKukGteUuB0rQ==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "6.17.0", + "Microsoft.IdentityModel.Tokens": "6.17.0" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "Azure.Storage.Common": { + "type": "Transitive", + "resolved": "12.10.0", + "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "dependencies": { + "Azure.Core": "1.22.0", + "System.IO.Hashing": "6.0.0" + } + }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.15.8", + "contentHash": "tA0S9QXJq+r3CjwBlcn5glEUrbdAxhPWO4yhq5+ycn6WW6+nsvqzO6Qf6NE9XWbEz/F2QSpBTxjdTI7SvVy7CQ==", + "dependencies": { + "System.Memory": "4.5.3", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.37.0", + "contentHash": "ubqW2nTpiHyDudYGVXM+Vjh6WbgsI1fVQxsDK14/GnyPgiNMuNl8+GQcAYp5QPhAk5H4fjHJPI+KvbpVk8z6iQ==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.37.0", + "contentHash": "oMDNXAPkBzfXljv/ZzlZSf22TEstBNI6je85/c3iztlFbbixTMLgi0sIu/uHtEKoEWUPr0nmBMvD+jtqKorGTg==", + "dependencies": { + "Grpc.Net.Common": "2.37.0", + "Microsoft.Extensions.Logging.Abstractions": "3.0.3" + } + }, + "Grpc.Net.ClientFactory": { + "type": "Transitive", + "resolved": "2.37.0", + "contentHash": "zyeFej1A36+s5K6+zDUirmDEGHEFnHapQisT7YsR9nQqKsw1uYqjtG1gSVSg/Zvk0KYeLHs5/URtTU71kS4APg==", + "dependencies": { + "Grpc.Net.Client": "2.37.0", + "Microsoft.Extensions.Http": "3.0.3" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.37.0", + "contentHash": "V7fZb+87qB6Jio6uWXDHkxI9WT+y4EFwicAHkzB2lm/9wJazD0V35HhQjxvoONsldObaUimjqd4b/XZ0G07sDQ==", + "dependencies": { + "Grpc.Core.Api": "2.37.0" + } + }, + "Microsoft.ApplicationInsights": { + "type": "Transitive", + "resolved": "2.20.0", + "contentHash": "mb+EC5j06Msn5HhKrhrsMAst6JxvYUnphQMGY2cixCabgGAO3q79Y8o/p1Zce1Azgd1IVkRKAMzAV4vDCbXOqA==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } + }, + "Microsoft.AspNetCore.Cryptography.Internal": { + "type": "Transitive", + "resolved": "5.0.8", + "contentHash": "giHheyNLOb+cAHpb8b0GhaS0xJ+hAIIDSyWPe5aOPwpgctsjOPRKFyn/268xv+zBVuEtyRJJEnBUlkOVzyIpZA==" + }, + "Microsoft.AspNetCore.DataProtection": { + "type": "Transitive", + "resolved": "5.0.8", + "contentHash": "wCMdfuKA+ePcB4nEDau5tNhhhC5NFa2LEXoRhk2Xaot13FFlyKA4t5UzIyV/OnAfB/bqbAIvChJD+biWY7u5SA==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.Internal": "5.0.8", + "Microsoft.AspNetCore.DataProtection.Abstractions": "5.0.8", + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging.Abstractions": "5.0.0", + "Microsoft.Extensions.Options": "5.0.0", + "Microsoft.Win32.Registry": "5.0.0", + "System.Security.Cryptography.Xml": "5.0.0" + } + }, + "Microsoft.AspNetCore.DataProtection.Abstractions": { + "type": "Transitive", + "resolved": "5.0.8", + "contentHash": "ZI9S2NGjuOKXN3PxJcF8EKVwd1cqpWyUSqiVoH8gqq5tlHaXULwPmoR0DBOFON4sEFETRWI69f5RQ3tJWw205A==" + }, + "Microsoft.Azure.Functions.Worker.Core": { + "type": "Transitive", + "resolved": "1.4.0", + "contentHash": "6fTSb6JDm+1CNKsaPziL36c3tfN4xxYnC9XoJsm0g9tY+72dVqUa2aPc6RtkwBmT5sjNrsUDlUC+IhG+ehjppQ==", + "dependencies": { + "Azure.Core": "1.10.0", + "Microsoft.Extensions.Hosting": "5.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "5.0.0" + } + }, + "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kAs9BTuzdOvyuN2m5CYyQzyvzXKJ6hhIOgcwm0W8Q+Fwj91a1eBmRSi9pVzpM4V3skNt/+pkPD3wxFD4nEw0bg==" + }, + "Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "Cr+ziBQA/Lyt5rMURHBje+foGook+B82gnAfEE32bHcXGlpKJnnVdLqHy0OqHliUksFddkRYS2gY8dYx+NH/mA==", + "dependencies": { + "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.1.0" + } + }, + "Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "cF95kiiU6PD9sptrV3GKQKzRv2DYATYNTpOtvUtbAYQ4xPFKgF4ke3fDBcu+cu2O1/C8FQ7MhzkEQv00bx552A==", + "dependencies": { + "Microsoft.Azure.Functions.Worker.Extensions.Abstractions": "1.1.0" + } + }, + "Microsoft.Azure.Functions.Worker.Grpc": { + "type": "Transitive", + "resolved": "1.3.1", + "contentHash": "lMlbyfRagSQZVWN73jnaB0tVTMhfKHSy6IvFXC7fCGh+7uA9LJNjcMOQbVkUnmvb/I/SxslMqD7xcebrxFL3TQ==", + "dependencies": { + "Azure.Core": "1.10.0", + "Google.Protobuf": "3.15.8", + "Grpc.Net.Client": "2.37.0", + "Grpc.Net.ClientFactory": "2.37.0", + "Microsoft.Azure.Functions.Worker.Core": "1.3.1", + "Microsoft.Extensions.Hosting": "5.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "5.0.0" + } + }, + "Microsoft.Azure.Functions.Worker.Sdk.Analyzers": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "J7AZ9iv/UCd4Di0c84h1P/Sa1aQr5uqO0EBUKwE0AZeWJ11dDfKAwxMiAxYOKR+giy31DWBnuFc4GKY3BQYUjg==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ==" + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "bu8As90/SBAouMZ6fJ+qRNo1X+KgHGrVueFhhYi+E5WqEhcnp2HoWRFnMzXQ6g4RdZbvPowFerSbKNH4Dtg5yg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "5.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "/1qPCleFOkJe0O+xmFqCNLFYQZTJz965sVw8CUB/BQgsApBwzAUsL2BUkDvQW+geRUVTXUS9zLa0pBjC2VJ1gA==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "5.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging.Abstractions": "5.0.0", + "Microsoft.Extensions.Options": "5.0.0", + "Microsoft.Extensions.Primitives": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "LN322qEKHjuVEhhXueTUe7RNePooZmS8aGid5aK2woX3NPjSnONFyKUc6+JknOS6ce6h2tCLfKPTBXE3mN/6Ag==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", + "Microsoft.Extensions.Primitives": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ETjSBHMp3OAZ4HxGQYpwyGsD8Sw5FegQXphi0rpoGMT74S4+I2mm7XJEswwn59XAaKOzC15oDSOWEE8SzDCd6Q==", + "dependencies": { + "Microsoft.Extensions.Primitives": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "Of1Irt1+NzWO+yEYkuDh5TpT4On7LKl98Q9iLqCdOZps6XXEWDj3AKtmyvzJPVXZe4apmkJJIiDL7rR1yC+hjQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "OelM+VQdhZ0XMXsEQBq/bt3kFzD+EBGqR4TAgFDRAye0JfvHAaRi+3BxCRcwqUAwDhV0U0HieljBGHlTgYseRA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "5.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "fqh6y6hAi0Z0fRsb4B/mP9OkKkSlifh5osa+N/YSQ+/S2a//+zYApZMUC1XeP9fdjlgZoPQoZ72Q2eLHyKLddQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "5.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "rRdspYKA18ViPOISwAihhCMbusHsARCOtDMwa23f+BGEdIjpKPlhs3LLjmKlxfhpGXBjIsS0JpXcChjRUN+PAw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "5.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0", + "Microsoft.Extensions.FileProviders.Physical": "5.0.0", + "Microsoft.Extensions.Primitives": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "Pak8ymSUfdzPfBTLHxeOwcR32YDbuVfhnH2hkfOLnJNQd19ItlBdpMjIDY9C5O/nS2Sn9bzDMai0ZrvF7KyY/Q==", + "dependencies": { + "Microsoft.Extensions.Configuration": "5.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "5.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "+tK3seG68106lN277YWQvqmfyI/89w0uTu/5Gz5VYSUu5TI4mqwsaWLlSmT9Bl1yW/i1Nr06gHJxqaqB5NU9Tw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", + "Microsoft.Extensions.Configuration.Json": "5.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0", + "Microsoft.Extensions.FileProviders.Physical": "5.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "Rc2kb/p3Ze6cP6rhFC3PJRdWGbLvSHZc0ev7YlyeU6FmHciDMLrhoVoTUEzKPhN5ZjFgKF1Cf5fOz8mCMIkvpA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "iuZIiZ3mteEb+nsUqpGXKx2cGF+cv6gWPd5jqQI4hzqdiJ6I94ddLjKhQOuRW1lueHwocIw30xbSHGhQj0zjdQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "5.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "1rkd8UO2qf21biwO7X0hL9uHP7vtfmdv/NLvKgCRHkdz1XnW8zVQJXyEYiN68WYpExgtVWn55QF0qBzgfh1mGg==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "5.0.0", + "Microsoft.Extensions.Primitives": "5.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ArliS8lGk8sWRtrWpqI8yUVYJpRruPjCDT+EIjrgkA/AAPRctlAkRISVZ334chAKktTLzD1+PK8F5IZpGedSqA==" + }, + "Microsoft.Extensions.Hosting": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "hiokSU1TOVfcqpQAnpiOzP2rE9p+niq92g5yeAnwlbSrUlIdIS6M8emCknZvhdOagQA9x5YWNwe1n0kFUwE0NQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "5.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", + "Microsoft.Extensions.Configuration.Binder": "5.0.0", + "Microsoft.Extensions.Configuration.CommandLine": "5.0.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "5.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "5.0.0", + "Microsoft.Extensions.Configuration.Json": "5.0.0", + "Microsoft.Extensions.Configuration.UserSecrets": "5.0.0", + "Microsoft.Extensions.DependencyInjection": "5.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0", + "Microsoft.Extensions.FileProviders.Physical": "5.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging": "5.0.0", + "Microsoft.Extensions.Logging.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging.Configuration": "5.0.0", + "Microsoft.Extensions.Logging.Console": "5.0.0", + "Microsoft.Extensions.Logging.Debug": "5.0.0", + "Microsoft.Extensions.Logging.EventLog": "5.0.0", + "Microsoft.Extensions.Logging.EventSource": "5.0.0", + "Microsoft.Extensions.Options": "5.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "cbUOCePYBl1UhM+N2zmDSUyJ6cODulbtUd9gEzMFIK3RQDtP/gJsE08oLcBSXH3Q1RAQ0ex7OAB3HeTKB9bXpg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0" + } + }, + "Microsoft.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.3", + "contentHash": "dcyB8szIcSynjVZRuFgqkZpPgTc5zeRSj1HMXSmNqWbHYKiPYJl8ZQgBHz6wmZNSUUNGpCs5uxUg8DZHHDC1Ew==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "3.0.3", + "Microsoft.Extensions.Logging": "3.0.3", + "Microsoft.Extensions.Options": "3.0.3" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "MgOwK6tPzB6YNH21wssJcw/2MKwee8b2gI7SllYfn6rvTpIrVvVS5HAjSU2vqSku1fwqRvWP0MdIi14qjd93Aw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "5.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging.Abstractions": "5.0.0", + "Microsoft.Extensions.Options": "5.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NxP6ahFcBnnSfwNBi2KH2Oz8Xl5Sm2krjId/jRR3I7teFphwiUoUeZPwTNA21EX+5PtjqmyAvKaOeBXcJjcH/w==" + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "N3/d0HeMRnBekadbZlmbp+In8EvNNkQHSdbtRzjrGVckdZWpYs5GNrAfaYqVplDFW0WUedSaFJ3khB50BWYGsw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "5.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", + "Microsoft.Extensions.Configuration.Binder": "5.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging": "5.0.0", + "Microsoft.Extensions.Logging.Abstractions": "5.0.0", + "Microsoft.Extensions.Options": "5.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "5.0.0" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "jH0wbWhfvXjOVmCkbra4vbiovDtTUIWLQjCeJ7Xun3h4AHvwfzm7V7wlsXKs3tNnPrsCxZ9oaV0vUAgGY1JxOA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging": "5.0.0", + "Microsoft.Extensions.Logging.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging.Configuration": "5.0.0", + "Microsoft.Extensions.Options": "5.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "5.0.0" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "9dvt0xqRrClvhaPNpfyS39WxnW9G55l5lrV5ZX7IrEgwo4VwtmJKtoPiKVYKbhAuOBGUI5WY3hWLvF+PSbJp5A==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging": "5.0.0", + "Microsoft.Extensions.Logging.Abstractions": "5.0.0" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "CYzsgF2lqgahGl/HuErsIDaZZ9ueN+MBjGfO/0jVDLPaXLaywxlGKFpDgXMaB053DRYZwD1H2Lb1I60mTXS3jg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging": "5.0.0", + "Microsoft.Extensions.Logging.Abstractions": "5.0.0", + "Microsoft.Extensions.Options": "5.0.0", + "System.Diagnostics.EventLog": "5.0.0" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "hF+D6PJkrM0qXcSEGs1BwZwgP8c0BRkj26P/5wmYTcHKOp52GRey/Z/YKRmRIHIrXxj9tz/JgIjU9oWmiJ5HMw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.Logging": "5.0.0", + "Microsoft.Extensions.Logging.Abstractions": "5.0.0", + "Microsoft.Extensions.Options": "5.0.0", + "Microsoft.Extensions.Primitives": "5.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "CBvR92TCJ5uBIdd9/HzDSrxYak+0W/3+yxrNg8Qm6Bmrkh5L+nu6m3WeazQehcZ5q1/6dDA7J5YdQjim0165zg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.Primitives": "5.0.0" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "280RxNJqOeQqq47aJLy5D9LN61CAWeuRA83gPToQ8B9jl9SNdQ5EXjlfvF66zQI5AXMl+C/3hGnbtIEN+X3mqA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "5.0.0", + "Microsoft.Extensions.Configuration.Binder": "5.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0", + "Microsoft.Extensions.Options": "5.0.0", + "Microsoft.Extensions.Primitives": "5.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "5WPSmL4YeP7eW+Vc8XZ4DwjYWBAiSwDV9Hm63JJWcz1Ie3Xjv4KuJXzgCstj48LkLfVCYa7mLcx7y+q6yqVvtw==" + }, + "Microsoft.Graph.Core": { + "type": "Transitive", + "resolved": "2.0.8", + "contentHash": "CcsCgY1O+LQaKMJkQCpVZPmK6uiFFUu49MwqTxoTBYtFG2/NwyGAIpla5gct8jWlS0DBKXWAyRlIzKt/6UGIDQ==", + "dependencies": { + "Azure.Core": "1.22.0", + "Microsoft.Identity.Client": "4.41.0", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.15.1", + "System.Diagnostics.DiagnosticSource": "4.7.1", + "System.Security.Claims": "4.3.0", + "System.Text.Json": "6.0.2" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "2.19.3", + "contentHash": "zVVZjn8aW7W79rC1crioDgdOwaFTQorsSO6RgVlDDjc7MvbEGz071wSNrjVhzR0CdQn6Sefx7Abf1o7vasmrLg==", + "dependencies": { + "Microsoft.Identity.Client": "4.38.0", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "6.17.0", + "contentHash": "I3cSVE185qF3a222/iQIdmBFhrhZBtz7wZ1RUUbMuHC1un79XCI7vggbWdmbqIttFcUoeziemadO6t+3FLjcSA==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "6.17.0" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "6.17.0", + "contentHash": "Ix6/CMLDoo939NDf1ARDuGK6YERY7pAX9WYbfwb4gZqx7r52unMFIykJk+zlEBX7jjtbDz/0uzikQFvheV9KsQ==" + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "6.15.1", + "contentHash": "6nHr+4yE8vj620Vy4L0pl7kmkvWc06wBrJ+AOo/gjqzu/UD/MYgySUqRGlZYrvvNmKkUWMw4hdn78MPCb4bstA==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "6.15.1", + "Microsoft.IdentityModel.Tokens": "6.15.1" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "6.15.1", + "contentHash": "WwecgT/PNrytLNUWjkYtnnG2LXMAzkINSaZM+8dPPiEpOGz1bQDBWAenTSurYICxGoA1sOPriFXk+ocnQyprKw==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "6.15.1", + "System.IdentityModel.Tokens.Jwt": "6.15.1" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "6.17.0", + "contentHash": "mhOe+d9BQg5U45TkTCyXAFOjl7RvwaFj6v9qo8b+WFolkuGsfjSFfQ+WI9D3ho9sD/fK75gvL4JptmjLzyUPkw==", + "dependencies": { + "Microsoft.CSharp": "4.5.0", + "Microsoft.IdentityModel.Logging": "6.17.0", + "System.Security.Cryptography.Cng": "4.5.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0" + } + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FHkCwUfsTs+/5tsK+c0egLfacUgbhvcwi3wUFWSEEArSXao343mYqcpOVVFMlcCkdNtjU4YwAWaKYwal6f02og==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "Microsoft.Win32.Registry": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Drawing.Common": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==", + "dependencies": { + "Microsoft.Win32.SystemEvents": "5.0.0" + } + }, + "System.Formats.Asn1": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w==" + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Rfm2jYCaUeGysFEZjDe7j1R4x6Z6BzumS/vUT5a1AA/AWJuGX71PoGB0RmpyX3VmrGqVnAwtfMn39OHR8Y/5+g==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "JGkzeqgBsiZwKJZ1IxPNsDFZDhUvuEdX8L8BDC8N3KOj+6zMcNU28CNN59TpZE/VJYy9cP+5M+sbxtWJx3/xtw==", + "dependencies": { + "System.Text.Encodings.Web": "4.7.2", + "System.Text.Json": "4.6.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Claims": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "P/+BR/2lnc4PNDHt/TPBAWHVMLMRHsyYZbU1NphW4HIWzCggz8mJbTQQ3MKljFE7LS3WagmVFuBgoLcFzYXlkA==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Security.Principal": "4.3.0" + } + }, + "System.Security.Cryptography.Cng": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==", + "dependencies": { + "System.Formats.Asn1": "5.0.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "9TPLGjBCGKmNvG8pjwPeuYy0SMVmGZRwlTZvyPHDbYv/DRkoeumJdfumaaDNQzVGMEmbWtg07zUpSW9q70IlDQ==", + "dependencies": { + "System.Formats.Asn1": "5.0.0", + "System.Security.Cryptography.Cng": "5.0.0" + } + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" + }, + "System.Security.Cryptography.Xml": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "MYmkHtCW+paFmPGFDktnLdOeH3zUrNchbZNki87E1ejNSMm9enSRbJokmvFrsWUrDE4bRE1lVeAle01+t6SGhA==", + "dependencies": { + "System.Security.Cryptography.Pkcs": "5.0.0", + "System.Security.Permissions": "5.0.0" + } + }, + "System.Security.Permissions": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Windows.Extensions": "5.0.0" + } + }, + "System.Security.Principal": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "6.0.2", + "contentHash": "0nE2gwXLn3PTBOPwORLqwuYvWB+Beomt9ZBX+6LmogMNKUvfD1SoDb/ycB1vBntT94rGaB/SvxEyeLu14H6aEg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "6.0.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" + }, + "System.Windows.Extensions": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==", + "dependencies": { + "System.Drawing.Common": "5.0.0" + } + } + } + } } \ No newline at end of file From 9710daeefb23b1a557262104c6459c66ea424c67 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 21 Apr 2022 17:17:37 -0700 Subject: [PATCH 03/25] Initial Push. Working on Testing. --- .../ApiService/OneFuzzTypes/Webhooks.cs | 9 ++- src/ApiService/ApiService/QueueWebhooks.cs | 21 +++--- src/ApiService/ApiService/onefuzzlib/Creds.cs | 4 +- .../ApiService/onefuzzlib/Storage.cs | 10 +-- .../onefuzzlib/WebhookMessageLogOperations.cs | 41 ++++++---- .../onefuzzlib/WebhookOperations.cs | 75 +++++++++++++++---- 6 files changed, 111 insertions(+), 49 deletions(-) diff --git a/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs b/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs index e0a0f19579..5d42949d48 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs @@ -10,6 +10,11 @@ public enum WebhookMessageFormat EventGrid } +public record WebhookMessageQueueObj( + Guid WebhookId, + Guid EventId + ); + public record WebhookMessage(Guid EventId, EventType EventType, BaseEvent Event, @@ -22,11 +27,11 @@ public record WebhookMessageEventGrid( [property: JsonPropertyName("dataVersion")] string DataVersion, string Subject, [property: JsonPropertyName("EventType")] EventType EventType, - [property: JsonPropertyName("eventTime")] DateTimeOffset eventTime, + [property: JsonPropertyName("eventTime")] DateTimeOffset EventTime, Guid Id, [property: TypeDiscrimnatorAttribute("EventType", typeof(EventTypeProvider))] [property: JsonConverter(typeof(BaseEventConverter))] - BaseEvent data); + BaseEvent data) : EntityBase(); public record WebhookMessageLog( [RowKey] Guid EventId, diff --git a/src/ApiService/ApiService/QueueWebhooks.cs b/src/ApiService/ApiService/QueueWebhooks.cs index 65b5b9a3f1..031fbc9a24 100644 --- a/src/ApiService/ApiService/QueueWebhooks.cs +++ b/src/ApiService/ApiService/QueueWebhooks.cs @@ -1,29 +1,28 @@ using System; using Microsoft.Azure.Functions.Worker; using System.Text.Json; -using System.Threading.Tasks; using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; namespace Microsoft.OneFuzz.Service; public class QueueWebhooks { - private readonly ILogTracerFactory _loggerFactory; - - public QueueWebhooks(ILogTracerFactory loggerFactory) + private readonly ILogTracer _log; + private readonly IWebhookMessageLogOperations _webhookMessageLog; + public QueueWebhooks(ILogTracer log, IWebhookMessageLogOperations webhookMessageLog) { - _loggerFactory = loggerFactory; + _log = log; + _webhookMessageLog = webhookMessageLog; } [Function("QueueWebhooks")] - public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")] string msg) + public async Async.Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")] string msg) { - var log = _loggerFactory.MakeLogTracer(Guid.NewGuid()); - log.Info($"Webhook Message Queued: {msg}"); + _log.Info($"Webhook Message Queued: {msg}"); + + var obj = JsonSerializer.Deserialize(msg, EntityConverter.GetJsonSerializerOptions()).EnsureNotNull($"wrong data {msg}"); - var obj = JsonSerializer.Deserialize(msg, EntityConverter.GetJsonSerializerOptions()).EnsureNotNull($"wrong data {msg}"); ; - - // WebhookMessageLog.process_from_queue(obj) + await _webhookMessageLog.ProcessFromQueue(obj); } } \ No newline at end of file diff --git a/src/ApiService/ApiService/onefuzzlib/Creds.cs b/src/ApiService/ApiService/onefuzzlib/Creds.cs index 3831526ffe..48610a1831 100644 --- a/src/ApiService/ApiService/onefuzzlib/Creds.cs +++ b/src/ApiService/ApiService/onefuzzlib/Creds.cs @@ -69,11 +69,11 @@ public string GetInstanceName() public async Async.Task GetInstanceId() { - var blob = await _containers.GetBlob("base-config", "instance_id", StorageType.Config); + var blob = await _containers.GetBlob(new Container("base-config"), "instance_id", StorageType.Config); if (blob == null) { throw new System.Exception("Blob Not Found"); } - return System.Guid.Parse(System.Text.Encoding.Default.GetString(blob)); + return System.Guid.Parse(System.Text.Encoding.Default.GetString(blob.ToArray())); } } diff --git a/src/ApiService/ApiService/onefuzzlib/Storage.cs b/src/ApiService/ApiService/onefuzzlib/Storage.cs index 8dc2c0d20d..a9d6077054 100644 --- a/src/ApiService/ApiService/onefuzzlib/Storage.cs +++ b/src/ApiService/ApiService/onefuzzlib/Storage.cs @@ -18,7 +18,6 @@ public interface IStorage public IEnumerable CorpusAccounts(); string GetPrimaryAccount(StorageType storageType); public (string?, string?) GetStorageAccountNameAndKey(string accountId); - public List GetAccounts(StorageType storageType); public IEnumerable GetAccounts(StorageType storageType); } @@ -122,9 +121,10 @@ public string ChooseAccounts(StorageType storageType) throw new Exception($"No Storage Accounts for {storageType}"); } - if (accounts.Count == 1) + var account_list = accounts.ToList(); + if (account_list.Count == 1) { - return accounts[0]; + return account_list[0]; } // Use a random secondary storage account if any are available. This @@ -133,9 +133,9 @@ public string ChooseAccounts(StorageType storageType) // // security note: this is not used as a security feature var random = new Random(); - var index = random.Next(accounts.Count); + var index = random.Next(account_list.Count); - return accounts[index]; // nosec + return account_list[index]; // nosec } public IEnumerable GetAccounts(StorageType storageType) diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs index 9a86d949c9..9efc19b692 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs @@ -11,17 +11,14 @@ namespace Microsoft.OneFuzz.Service; public interface IWebhookMessageLogOperations : IOrm { IAsyncEnumerable SearchExpired(); + public Async.Task ProcessFromQueue(WebhookMessageQueueObj obj); } public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations { const int EXPIRE_DAYS = 7; - - record WebhookMessageQueueObj( - Guid WebhookId, - Guid EventId - ); + const int MAX_TRIES = 5; private readonly IQueue _queue; private readonly ILogTracer _log; @@ -61,7 +58,7 @@ public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) } } - public async Task ProcessFromQueue(WebhookMessageQueueObj obj) + public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) { var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); @@ -73,13 +70,13 @@ public async Task ProcessFromQueue(WebhookMessageQueueObj obj) ("EventId", obj.EventId.ToString()) } ). Error($"webhook message log not found for webhookId: {obj.WebhookId} and eventId: {obj.EventId}"); - return null; + } else + { + Process(message); } - - Process(message); } - private void Process(WebhookMessageLog message) + private async void Process(WebhookMessageLog message) { if (message.State == WebhookMessageState.Failed || message.State == WebhookMessageState.Succeeded) @@ -92,14 +89,31 @@ private void Process(WebhookMessageLog message) Error($"webhook message already handled. {message.WebhookId}:{message.EventId}"); } - // message.TryCount++; var newMessage = message with { TryCount = message.TryCount + 1 }; _log.Info($"sending webhook: {message.WebhookId}:{message.EventId}"); + var success = await Send(newMessage); + if (success) + { + newMessage = newMessage with { State = WebhookMessageState.Succeeded }; + await Replace(newMessage); + _log.Info($"sent webhook event {newMessage.WebhookId}:{newMessage.EventId}"); + } else if (newMessage.TryCount < MAX_TRIES) + { + newMessage = newMessage with { State = WebhookMessageState.Retrying }; + await Replace(newMessage); + await QueueWebhook(newMessage); + _log.Warning($"sending webhook event failed, re-queued {newMessage.WebhookId}:{newMessage.EventId}"); + } else + { + newMessage = newMessage with { State = WebhookMessageState.Failed }; + await Replace(newMessage); + _log.Info($"sending webhook: {newMessage.WebhookId} event: {newMessage.EventId} failed {newMessage.TryCount} times."); + } } - private async Task Send(WebhookMessageLog message) + private async Task Send(WebhookMessageLog message) { var webhook = await _webhook.GetByWebhookId(message.WebhookId); if (webhook == null) @@ -115,7 +129,7 @@ private void Process(WebhookMessageLog message) try { - return _webhook.Send(message); + return await _webhook.Send(message); } catch (Exception) { @@ -125,6 +139,7 @@ private void Process(WebhookMessageLog message) } ). Error($"webhook send failed. {message.WebhookId}"); + return false; } } diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index c41d8db010..938ddeb7e5 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -1,7 +1,9 @@ +using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; using ApiService.OneFuzzLib.Orm; -using System; -using System.Collections.Generic; -using System.Linq; +using System.Text.Json; +using System.Security.Cryptography; +using System.Net; +using System.Text; namespace Microsoft.OneFuzz.Service; @@ -9,19 +11,24 @@ public interface IWebhookOperations { Async.Task SendEvent(EventMessage eventMessage); Async.Task GetByWebhookId(Guid webhookId); - Async.Task Send(WebhookMessageLog messageLog); + Async.Task Send(WebhookMessageLog messageLog); } public class WebhookOperations : Orm, IWebhookOperations { + + // Needs to eventually pull from global __version__ + const string USER_AGENT = "onefuzz-webhook 0.0.0"; private readonly IWebhookMessageLogOperations _webhookMessageLogOperations; private readonly ILogTracer _log; + private ICreds _creds; - public WebhookOperations(IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, ILogTracer log) + public WebhookOperations(ICreds creds, IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, ILogTracer log) : base(storage) { _webhookMessageLogOperations = webhookMessageLogOperations; _log = log; + _creds = creds; } async public Async.Task SendEvent(EventMessage eventMessage) @@ -55,25 +62,61 @@ async private Async.Task AddEvent(Webhook webhook, EventMessage eventMessage) } } - public async Async.Task Send(WebhookMessageLog messageLog) + public async Async.Task Send(WebhookMessageLog messageLog) { var webhook = await GetByWebhookId(messageLog.WebhookId); - if (webhook == null) + if (webhook == null || webhook.Url == null) { - throw new Exception($"Webhook with WebhookId: {messageLog.WebhookId} Not Found"); + throw new Exception($"Invalid Webhook. Webhook with WebhookId: {messageLog.WebhookId} Not Found"); } + var (data, digest) = await BuildMessage(webhookId: webhook.WebhookId, eventId: messageLog.EventId, eventType: messageLog.EventType, webhookEvent: messageLog.Event, secretToken: webhook.SecretToken, messageFormat: webhook.MessageFormat); + + var headers = new Dictionary {{"Content-type", "application/json"}, {"User-Agent", USER_AGENT}}; + + if (digest != null) + { + headers["X-Onefuzz-Digest"] = digest; + } + + var client = new Request(); + var response = client.Post(url: webhook.Url, json: data, headers: headers); + var result = response.Result; + if (result.StatusCode == HttpStatusCode.Accepted) + { + return true; + } + return false; } - // public Tuple BuildMessage(Guid webhookId, Guid eventId, EventType eventType, Event webhookEvent, String? SeretToken, WebhookMessageFormat? messageFormat) - // { - // if (messageFormat != null && messageFormat == WebhookMessageFormat.EventGrid) - // { - // var eventGridMessage = new WebhookMessageEventGrid(Id: eventId, data: webhookEvent, DataVersion: "1.0.0", Subject: ) - // // var decoded = [JsonSerializer.Serialize()] - // } - // } + // Not converting to bytes, as it's not neccessary in C#. Just keeping as string. + public async Async.Task> BuildMessage(Guid webhookId, Guid eventId, EventType eventType, BaseEvent webhookEvent, String? secretToken, WebhookMessageFormat? messageFormat) + { + var entityConverter = new EntityConverter(); + string data = ""; + if (messageFormat != null && messageFormat == WebhookMessageFormat.EventGrid) + { + var eventGridMessage = new [] {new WebhookMessageEventGrid(Id: eventId, data: webhookEvent, DataVersion: "1.0.0", Subject: _creds.GetInstanceName(), EventType: eventType, EventTime: DateTimeOffset.UtcNow) }; + data = JsonSerializer.Serialize(eventGridMessage, options: EntityConverter.GetJsonSerializerOptions()); + } else + { + var instanceId = await _creds.GetInstanceId(); + var webhookMessage = new WebhookMessage(WebhookId: webhookId, EventId: eventId, EventType: eventType, Event: webhookEvent, InstanceId: instanceId, InstanceName: _creds.GetInstanceName()); + data = JsonSerializer.Serialize(webhookMessage, options: EntityConverter.GetJsonSerializerOptions()); + } + + string? digest = null; + var hmac = HMAC.Create("HMACSHA512"); + if (secretToken != null && hmac != null) + { + hmac.Key = System.Text.Encoding.UTF8.GetBytes(secretToken); + digest = Convert.ToHexString(hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(data))); + } + + return new Tuple (data, digest); + } + public async Async.Task GetByWebhookId(Guid webhookId) { var data = QueryAsync(filter: $"PartitionKey eq '{webhookId}'"); From 2c389ce56ecd073585bb8aa308abb13c2d156a72 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 22 Apr 2022 09:50:44 -0700 Subject: [PATCH 04/25] Moving InstanceId --- src/ApiService/ApiService/onefuzzlib/Creds.cs | 11 ----------- src/ApiService/ApiService/onefuzzlib/Storage.cs | 16 +++++++++++++++- .../onefuzzlib/WebhookMessageLogOperations.cs | 2 +- .../ApiService/onefuzzlib/WebhookOperations.cs | 6 ++++-- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/Creds.cs b/src/ApiService/ApiService/onefuzzlib/Creds.cs index e42ebe3870..d8e6a82850 100644 --- a/src/ApiService/ApiService/onefuzzlib/Creds.cs +++ b/src/ApiService/ApiService/onefuzzlib/Creds.cs @@ -18,8 +18,6 @@ public interface ICreds public string GetInstanceName(); - public Async.Task GetInstanceId(); - public ArmClient ArmClient { get; } public ResourceGroupResource GetResourceGroupResource(); @@ -80,15 +78,6 @@ public string GetInstanceName() return instanceName; } - - public async Async.Task GetInstanceId() - { - var blob = await _containers.GetBlob(new Container("base-config"), "instance_id", StorageType.Config); - if (blob == null) - { - throw new System.Exception("Blob Not Found"); - } - return System.Guid.Parse(System.Text.Encoding.Default.GetString(blob.ToArray())); public ResourceGroupResource GetResourceGroupResource() { diff --git a/src/ApiService/ApiService/onefuzzlib/Storage.cs b/src/ApiService/ApiService/onefuzzlib/Storage.cs index 94f64b2e23..69a21a988d 100644 --- a/src/ApiService/ApiService/onefuzzlib/Storage.cs +++ b/src/ApiService/ApiService/onefuzzlib/Storage.cs @@ -17,6 +17,7 @@ public interface IStorage string GetPrimaryAccount(StorageType storageType); public (string?, string?) GetStorageAccountNameAndKey(string accountId); public IEnumerable GetAccounts(StorageType storageType); + public Async.Task GetInstanceId(); } public class Storage : IStorage @@ -24,12 +25,14 @@ public class Storage : IStorage private ICreds _creds; private ArmClient _armClient; private ILogTracer _log; + private IContainers _containers; - public Storage(ICreds creds, ILogTracer log) + public Storage(ICreds creds, ILogTracer log, IContainers containers) { _creds = creds; _armClient = new ArmClient(credential: _creds.GetIdentity(), defaultSubscriptionId: _creds.GetSubcription()); _log = log; + _containers = containers; } public static string GetFuncStorage() @@ -148,4 +151,15 @@ public IEnumerable GetAccounts(StorageType storageType) throw new NotImplementedException(); } } + + // Moved From Creds.cs + public async Async.Task GetInstanceId() + { + var blob = await _containers.GetBlob(new Container("base-config"), "instance_id", StorageType.Config); + if (blob == null) + { + throw new System.Exception("Blob Not Found"); + } + return System.Guid.Parse(System.Text.Encoding.Default.GetString(blob.ToArray())); + } } diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs index 9efc19b692..bf71c5de39 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs @@ -24,7 +24,7 @@ public class WebhookMessageLogOperations : Orm, IWebhookMessa private readonly ILogTracer _log; private readonly IWebhookOperations _webhook; - public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IWebhookOperations webhook) : base(storage) + public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IWebhookOperations webhook) : base(storage, log) { _queue = queue; _log = log; diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index c0b70508f3..0e3f4be5f2 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -22,13 +22,15 @@ public class WebhookOperations : Orm, IWebhookOperations private readonly IWebhookMessageLogOperations _webhookMessageLogOperations; private readonly ILogTracer _log; private ICreds _creds; + private readonly IStorage _storage; public WebhookOperations(ICreds creds, IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, ILogTracer log) - : base(storage) + : base(storage, log) { _webhookMessageLogOperations = webhookMessageLogOperations; _log = log; _creds = creds; + _storage = storage; } async public Async.Task SendEvent(EventMessage eventMessage) @@ -100,7 +102,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) data = JsonSerializer.Serialize(eventGridMessage, options: EntityConverter.GetJsonSerializerOptions()); } else { - var instanceId = await _creds.GetInstanceId(); + var instanceId = await _storage.GetInstanceId(); var webhookMessage = new WebhookMessage(WebhookId: webhookId, EventId: eventId, EventType: eventType, Event: webhookEvent, InstanceId: instanceId, InstanceName: _creds.GetInstanceName()); data = JsonSerializer.Serialize(webhookMessage, options: EntityConverter.GetJsonSerializerOptions()); } From 73b34a722334ac600ef1ba1e59d2b4e3d4e128ab Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 22 Apr 2022 09:51:42 -0700 Subject: [PATCH 05/25] Removing primitives. --- src/ApiService/ApiService/OneFuzzTypes/Primitives.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/ApiService/ApiService/OneFuzzTypes/Primitives.cs diff --git a/src/ApiService/ApiService/OneFuzzTypes/Primitives.cs b/src/ApiService/ApiService/OneFuzzTypes/Primitives.cs deleted file mode 100644 index e69de29bb2..0000000000 From eadd8204134179f7338495f6370a25da5f5112f2 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 22 Apr 2022 09:55:56 -0700 Subject: [PATCH 06/25] Formatting. --- .../ApiService/OneFuzzTypes/Model.cs | 14 +++--- src/ApiService/ApiService/QueueWebhooks.cs | 1 - src/ApiService/ApiService/onefuzzlib/Creds.cs | 5 +-- .../ApiService/onefuzzlib/Storage.cs | 4 +- .../onefuzzlib/WebhookMessageLogOperations.cs | 45 +++++++++---------- .../onefuzzlib/WebhookOperations.cs | 18 ++++---- 6 files changed, 42 insertions(+), 45 deletions(-) diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index 7f5d22a370..a530a99783 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -234,8 +234,8 @@ NodeTaskState State ); -public record Task( - // Timestamp: Optional[datetime] = Field(alias="Timestamp") +public record Task( + // Timestamp: Optional[datetime] = Field(alias="Timestamp") [PartitionKey] Guid JobId, [RowKey] Guid TaskId, TaskState State, @@ -302,11 +302,11 @@ Guid[] AllowedGroups public record InstanceConfig ( - [PartitionKey, RowKey] string InstanceName, - //# initial set of admins can only be set during deployment. - //# if admins are set, only admins can update instance configs. - Guid[]? Admins, - //# if set, only admins can manage pools or scalesets + [PartitionKey, RowKey] string InstanceName, + //# initial set of admins can only be set during deployment. + //# if admins are set, only admins can update instance configs. + Guid[]? Admins, + //# if set, only admins can manage pools or scalesets bool AllowPoolManagement, string[] AllowedAadTenants, NetworkConfig NetworkConfig, diff --git a/src/ApiService/ApiService/QueueWebhooks.cs b/src/ApiService/ApiService/QueueWebhooks.cs index 031fbc9a24..99d6e7e543 100644 --- a/src/ApiService/ApiService/QueueWebhooks.cs +++ b/src/ApiService/ApiService/QueueWebhooks.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.Azure.Functions.Worker; using System.Text.Json; using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; diff --git a/src/ApiService/ApiService/onefuzzlib/Creds.cs b/src/ApiService/ApiService/onefuzzlib/Creds.cs index d8e6a82850..8ff6c9e489 100644 --- a/src/ApiService/ApiService/onefuzzlib/Creds.cs +++ b/src/ApiService/ApiService/onefuzzlib/Creds.cs @@ -1,6 +1,5 @@ using Azure.Identity; using Azure.Core; -using System; using Azure.ResourceManager; using Azure.ResourceManager.Resources; @@ -77,8 +76,8 @@ public string GetInstanceName() ?? throw new System.Exception("Instance Name env var is not present"); return instanceName; - } - + } + public ResourceGroupResource GetResourceGroupResource() { var resourceId = GetResourceGroupResourceIdentifier(); diff --git a/src/ApiService/ApiService/onefuzzlib/Storage.cs b/src/ApiService/ApiService/onefuzzlib/Storage.cs index 69a21a988d..c6ca41be71 100644 --- a/src/ApiService/ApiService/onefuzzlib/Storage.cs +++ b/src/ApiService/ApiService/onefuzzlib/Storage.cs @@ -137,8 +137,8 @@ public string ChooseAccounts(StorageType storageType) var index = random.Next(account_list.Count); return account_list[index]; // nosec - } - + } + public IEnumerable GetAccounts(StorageType storageType) { switch (storageType) diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs index bf71c5de39..f430c0c88f 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs @@ -1,9 +1,5 @@ using ApiService.OneFuzzLib.Orm; -using Microsoft.OneFuzz.Service; -using System; -using System.Collections.Generic; using System.Threading.Tasks; -using System.Linq; namespace Microsoft.OneFuzz.Service; @@ -22,8 +18,8 @@ public class WebhookMessageLogOperations : Orm, IWebhookMessa private readonly IQueue _queue; private readonly ILogTracer _log; - private readonly IWebhookOperations _webhook; - + private readonly IWebhookOperations _webhook; + public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IWebhookOperations webhook) : base(storage, log) { _queue = queue; @@ -60,8 +56,8 @@ public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) { - var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); - + var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); + if (message == null) { _log.WithTags( @@ -70,15 +66,16 @@ public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) ("EventId", obj.EventId.ToString()) } ). Error($"webhook message log not found for webhookId: {obj.WebhookId} and eventId: {obj.EventId}"); - } else + } + else { - Process(message); + Process(message); } } - private async void Process(WebhookMessageLog message) - { - + private async System.Threading.Tasks.Task Process(WebhookMessageLog message) + { + if (message.State == WebhookMessageState.Failed || message.State == WebhookMessageState.Succeeded) { _log.WithTags( @@ -86,11 +83,11 @@ private async void Process(WebhookMessageLog message) ("WebhookId", message.WebhookId.ToString()), ("EventId", message.EventId.ToString()) } ). - Error($"webhook message already handled. {message.WebhookId}:{message.EventId}"); + Error($"webhook message already handled. {message.WebhookId}:{message.EventId}"); } - var newMessage = message with { TryCount = message.TryCount + 1 }; - + var newMessage = message with { TryCount = message.TryCount + 1 }; + _log.Info($"sending webhook: {message.WebhookId}:{message.EventId}"); var success = await Send(newMessage); if (success) @@ -98,24 +95,26 @@ private async void Process(WebhookMessageLog message) newMessage = newMessage with { State = WebhookMessageState.Succeeded }; await Replace(newMessage); _log.Info($"sent webhook event {newMessage.WebhookId}:{newMessage.EventId}"); - } else if (newMessage.TryCount < MAX_TRIES) + } + else if (newMessage.TryCount < MAX_TRIES) { newMessage = newMessage with { State = WebhookMessageState.Retrying }; await Replace(newMessage); await QueueWebhook(newMessage); _log.Warning($"sending webhook event failed, re-queued {newMessage.WebhookId}:{newMessage.EventId}"); - } else + } + else { newMessage = newMessage with { State = WebhookMessageState.Failed }; await Replace(newMessage); _log.Info($"sending webhook: {newMessage.WebhookId} event: {newMessage.EventId} failed {newMessage.TryCount} times."); - } - + } + } private async Task Send(WebhookMessageLog message) { - var webhook = await _webhook.GetByWebhookId(message.WebhookId); + var webhook = await _webhook.GetByWebhookId(message.WebhookId); if (webhook == null) { _log.WithTags( @@ -123,11 +122,11 @@ private async Task Send(WebhookMessageLog message) ("WebhookId", message.WebhookId.ToString()), } ). - Error($"webhook not found for webhookId: {message.WebhookId}"); + Error($"webhook not found for webhookId: {message.WebhookId}"); return false; } - try + try { return await _webhook.Send(message); } diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index 0e3f4be5f2..306de748e1 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -3,7 +3,6 @@ using System.Text.Json; using System.Security.Cryptography; using System.Net; -using System.Text; namespace Microsoft.OneFuzz.Service; @@ -23,7 +22,7 @@ public class WebhookOperations : Orm, IWebhookOperations private readonly ILogTracer _log; private ICreds _creds; private readonly IStorage _storage; - + public WebhookOperations(ICreds creds, IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, ILogTracer log) : base(storage, log) { @@ -74,7 +73,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) var (data, digest) = await BuildMessage(webhookId: webhook.WebhookId, eventId: messageLog.EventId, eventType: messageLog.EventType, webhookEvent: messageLog.Event, secretToken: webhook.SecretToken, messageFormat: webhook.MessageFormat); - var headers = new Dictionary {{"Content-type", "application/json"}, {"User-Agent", USER_AGENT}}; + var headers = new Dictionary { { "Content-type", "application/json" }, { "User-Agent", USER_AGENT } }; if (digest != null) { @@ -98,27 +97,28 @@ public async Async.Task Send(WebhookMessageLog messageLog) string data = ""; if (messageFormat != null && messageFormat == WebhookMessageFormat.EventGrid) { - var eventGridMessage = new [] {new WebhookMessageEventGrid(Id: eventId, data: webhookEvent, DataVersion: "1.0.0", Subject: _creds.GetInstanceName(), EventType: eventType, EventTime: DateTimeOffset.UtcNow) }; + var eventGridMessage = new[] { new WebhookMessageEventGrid(Id: eventId, data: webhookEvent, DataVersion: "1.0.0", Subject: _creds.GetInstanceName(), EventType: eventType, EventTime: DateTimeOffset.UtcNow) }; data = JsonSerializer.Serialize(eventGridMessage, options: EntityConverter.GetJsonSerializerOptions()); - } else + } + else { var instanceId = await _storage.GetInstanceId(); var webhookMessage = new WebhookMessage(WebhookId: webhookId, EventId: eventId, EventType: eventType, Event: webhookEvent, InstanceId: instanceId, InstanceName: _creds.GetInstanceName()); - data = JsonSerializer.Serialize(webhookMessage, options: EntityConverter.GetJsonSerializerOptions()); + data = JsonSerializer.Serialize(webhookMessage, options: EntityConverter.GetJsonSerializerOptions()); } string? digest = null; var hmac = HMAC.Create("HMACSHA512"); if (secretToken != null && hmac != null) { - hmac.Key = System.Text.Encoding.UTF8.GetBytes(secretToken); + hmac.Key = System.Text.Encoding.UTF8.GetBytes(secretToken); digest = Convert.ToHexString(hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(data))); } - return new Tuple (data, digest); + return new Tuple(data, digest); } - + public async Async.Task GetByWebhookId(Guid webhookId) { var data = QueryAsync(filter: $"PartitionKey eq '{webhookId}'"); From ce083d637bd16df4959d1c0482d3327482946da2 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 22 Apr 2022 10:00:23 -0700 Subject: [PATCH 07/25] Fixing formatting? --- src/ApiService/ApiService/OneFuzzTypes/Model.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index a530a99783..dcc52bbd7e 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -1,4 +1,4 @@ -using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; + using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; using System.Text.Json.Serialization; using Region = System.String; using PoolName = System.String; @@ -234,8 +234,7 @@ NodeTaskState State ); -public record Task( - // Timestamp: Optional[datetime] = Field(alias="Timestamp") +public record Task( [PartitionKey] Guid JobId, [RowKey] Guid TaskId, TaskState State, @@ -302,11 +301,11 @@ Guid[] AllowedGroups public record InstanceConfig ( - [PartitionKey, RowKey] string InstanceName, - //# initial set of admins can only be set during deployment. - //# if admins are set, only admins can update instance configs. - Guid[]? Admins, - //# if set, only admins can manage pools or scalesets + [PartitionKey, RowKey] string InstanceName, + //# initial set of admins can only be set during deployment. + //# if admins are set, only admins can update instance configs. + Guid[]? Admins, + //# if set, only admins can manage pools or scalesets bool AllowPoolManagement, string[] AllowedAadTenants, NetworkConfig NetworkConfig, From 16b16dfa6022025476a0aa3582374a6c3dd8d7fb Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 22 Apr 2022 10:06:44 -0700 Subject: [PATCH 08/25] Moving GetInstanceId to containers. --- .../ApiService/onefuzzlib/Containers.cs | 12 ++++++++++++ src/ApiService/ApiService/onefuzzlib/Storage.cs | 16 ++-------------- .../ApiService/onefuzzlib/WebhookOperations.cs | 8 ++++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/Containers.cs b/src/ApiService/ApiService/onefuzzlib/Containers.cs index 4de464b0b8..6f06c60059 100644 --- a/src/ApiService/ApiService/onefuzzlib/Containers.cs +++ b/src/ApiService/ApiService/onefuzzlib/Containers.cs @@ -14,6 +14,7 @@ public interface IContainers public Uri GetFileSasUrl(Container container, string name, StorageType storageType, bool read = false, bool add = false, bool create = false, bool write = false, bool delete = false, bool delete_previous_version = false, bool tag = false, int days = 30, int hours = 0, int minutes = 0); + public Async.Task GetInstanceId(); } public class Containers : IContainers @@ -89,4 +90,15 @@ public Uri GetFileSasUrl(Container container, string name, StorageType storageTy { throw new NotImplementedException(); } + + // Moved From Creds.cs + public async Async.Task GetInstanceId() + { + var blob = await GetBlob(new Container("base-config"), "instance_id", StorageType.Config); + if (blob == null) + { + throw new System.Exception("Blob Not Found"); + } + return System.Guid.Parse(System.Text.Encoding.Default.GetString(blob.ToArray())); + } } diff --git a/src/ApiService/ApiService/onefuzzlib/Storage.cs b/src/ApiService/ApiService/onefuzzlib/Storage.cs index c6ca41be71..72ee2c576b 100644 --- a/src/ApiService/ApiService/onefuzzlib/Storage.cs +++ b/src/ApiService/ApiService/onefuzzlib/Storage.cs @@ -17,7 +17,6 @@ public interface IStorage string GetPrimaryAccount(StorageType storageType); public (string?, string?) GetStorageAccountNameAndKey(string accountId); public IEnumerable GetAccounts(StorageType storageType); - public Async.Task GetInstanceId(); } public class Storage : IStorage @@ -137,8 +136,8 @@ public string ChooseAccounts(StorageType storageType) var index = random.Next(account_list.Count); return account_list[index]; // nosec - } - + } + public IEnumerable GetAccounts(StorageType storageType) { switch (storageType) @@ -151,15 +150,4 @@ public IEnumerable GetAccounts(StorageType storageType) throw new NotImplementedException(); } } - - // Moved From Creds.cs - public async Async.Task GetInstanceId() - { - var blob = await _containers.GetBlob(new Container("base-config"), "instance_id", StorageType.Config); - if (blob == null) - { - throw new System.Exception("Blob Not Found"); - } - return System.Guid.Parse(System.Text.Encoding.Default.GetString(blob.ToArray())); - } } diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index 306de748e1..e76d796f12 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -21,15 +21,15 @@ public class WebhookOperations : Orm, IWebhookOperations private readonly IWebhookMessageLogOperations _webhookMessageLogOperations; private readonly ILogTracer _log; private ICreds _creds; - private readonly IStorage _storage; + private readonly IContainers _containers; - public WebhookOperations(ICreds creds, IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, ILogTracer log) + public WebhookOperations(ICreds creds, IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, IContainers containers, ILogTracer log) : base(storage, log) { _webhookMessageLogOperations = webhookMessageLogOperations; _log = log; _creds = creds; - _storage = storage; + _containers = containers; } async public Async.Task SendEvent(EventMessage eventMessage) @@ -102,7 +102,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) } else { - var instanceId = await _storage.GetInstanceId(); + var instanceId = await _containers.GetInstanceId(); var webhookMessage = new WebhookMessage(WebhookId: webhookId, EventId: eventId, EventType: eventType, Event: webhookEvent, InstanceId: instanceId, InstanceName: _creds.GetInstanceName()); data = JsonSerializer.Serialize(webhookMessage, options: EntityConverter.GetJsonSerializerOptions()); } From 2c1038136b7eec0c68d3475f45d2ff2cf4f82c82 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 22 Apr 2022 10:10:23 -0700 Subject: [PATCH 09/25] Moving comments for formatting. --- src/ApiService/ApiService/OneFuzzTypes/Model.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index dcc52bbd7e..5c22025cb2 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -1,4 +1,4 @@ - using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; +using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; using System.Text.Json.Serialization; using Region = System.String; using PoolName = System.String; @@ -299,13 +299,13 @@ public record ApiAccessRule( Guid[] AllowedGroups ); +//# initial set of admins can only be set during deployment. +//# if admins are set, only admins can update instance configs. +//# if set, only admins can manage pools or scalesets public record InstanceConfig ( [PartitionKey, RowKey] string InstanceName, - //# initial set of admins can only be set during deployment. - //# if admins are set, only admins can update instance configs. Guid[]? Admins, - //# if set, only admins can manage pools or scalesets bool AllowPoolManagement, string[] AllowedAadTenants, NetworkConfig NetworkConfig, From e96c884fd3b174771d62f0f02d994aa255c0376f Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 22 Apr 2022 10:13:03 -0700 Subject: [PATCH 10/25] Removing unused dependency. --- src/ApiService/ApiService/onefuzzlib/Creds.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/Creds.cs b/src/ApiService/ApiService/onefuzzlib/Creds.cs index 8ff6c9e489..cea7a60359 100644 --- a/src/ApiService/ApiService/onefuzzlib/Creds.cs +++ b/src/ApiService/ApiService/onefuzzlib/Creds.cs @@ -33,12 +33,6 @@ public Creds() _armClient = new Lazy(() => new ArmClient(this.GetIdentity(), this.GetSubcription()), true); } - private IContainers _containers; - - public Creds(IContainers containers) - { - _containers = containers; - } // TODO: @cached public DefaultAzureCredential GetIdentity() { @@ -76,8 +70,8 @@ public string GetInstanceName() ?? throw new System.Exception("Instance Name env var is not present"); return instanceName; - } - + } + public ResourceGroupResource GetResourceGroupResource() { var resourceId = GetResourceGroupResourceIdentifier(); From 453e945e286d914bf31b83cfc283763e3dbb5efd Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 22 Apr 2022 10:15:57 -0700 Subject: [PATCH 11/25] Comments. --- .../onefuzzlib/WebhookMessageLogOperations.cs | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs index f430c0c88f..85ec9527dd 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs @@ -18,8 +18,8 @@ public class WebhookMessageLogOperations : Orm, IWebhookMessa private readonly IQueue _queue; private readonly ILogTracer _log; - private readonly IWebhookOperations _webhook; - + private readonly IWebhookOperations _webhook; + public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IWebhookOperations webhook) : base(storage, log) { _queue = queue; @@ -56,8 +56,8 @@ public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) { - var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); - + var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); + if (message == null) { _log.WithTags( @@ -66,16 +66,16 @@ public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) ("EventId", obj.EventId.ToString()) } ). Error($"webhook message log not found for webhookId: {obj.WebhookId} and eventId: {obj.EventId}"); - } - else + } + else { - Process(message); + await Process(message); } } private async System.Threading.Tasks.Task Process(WebhookMessageLog message) - { - + { + if (message.State == WebhookMessageState.Failed || message.State == WebhookMessageState.Succeeded) { _log.WithTags( @@ -83,11 +83,12 @@ private async System.Threading.Tasks.Task Process(WebhookMessageLog message) ("WebhookId", message.WebhookId.ToString()), ("EventId", message.EventId.ToString()) } ). - Error($"webhook message already handled. {message.WebhookId}:{message.EventId}"); + Error($"webhook message already handled. {message.WebhookId}:{message.EventId}"); + return; } - var newMessage = message with { TryCount = message.TryCount + 1 }; - + var newMessage = message with { TryCount = message.TryCount + 1 }; + _log.Info($"sending webhook: {message.WebhookId}:{message.EventId}"); var success = await Send(newMessage); if (success) @@ -95,26 +96,26 @@ private async System.Threading.Tasks.Task Process(WebhookMessageLog message) newMessage = newMessage with { State = WebhookMessageState.Succeeded }; await Replace(newMessage); _log.Info($"sent webhook event {newMessage.WebhookId}:{newMessage.EventId}"); - } + } else if (newMessage.TryCount < MAX_TRIES) { newMessage = newMessage with { State = WebhookMessageState.Retrying }; await Replace(newMessage); await QueueWebhook(newMessage); _log.Warning($"sending webhook event failed, re-queued {newMessage.WebhookId}:{newMessage.EventId}"); - } + } else { newMessage = newMessage with { State = WebhookMessageState.Failed }; await Replace(newMessage); _log.Info($"sending webhook: {newMessage.WebhookId} event: {newMessage.EventId} failed {newMessage.TryCount} times."); - } - + } + } private async Task Send(WebhookMessageLog message) { - var webhook = await _webhook.GetByWebhookId(message.WebhookId); + var webhook = await _webhook.GetByWebhookId(message.WebhookId); if (webhook == null) { _log.WithTags( @@ -122,11 +123,11 @@ private async Task Send(WebhookMessageLog message) ("WebhookId", message.WebhookId.ToString()), } ). - Error($"webhook not found for webhookId: {message.WebhookId}"); + Error($"webhook not found for webhookId: {message.WebhookId}"); return false; } - try + try { return await _webhook.Send(message); } From 31b4a57beb039b8f360bd7385e513472ef9e2a16 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 22 Apr 2022 10:33:25 -0700 Subject: [PATCH 12/25] Add WebhookEventGrid test. --- src/ApiService/Tests/OrmModelsTest.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/ApiService/Tests/OrmModelsTest.cs b/src/ApiService/Tests/OrmModelsTest.cs index e7b3fc3b26..8b633568d6 100644 --- a/src/ApiService/Tests/OrmModelsTest.cs +++ b/src/ApiService/Tests/OrmModelsTest.cs @@ -251,6 +251,22 @@ public static Gen WebhookMessage() } + public static Gen WebhookMessageEventGrid() + { + return Arb.Generate>().Select( + arg => + new WebhookMessageEventGrid( + DataVersion: arg.Item1, + Subject: arg.Item1, + EventType: arg.Item2, + EventTime: arg.Item3, + Id: arg.Item4, + data: arg.Item5 + ) + ); + + } + public static Gen Report() { return Arb.Generate, Guid, int>>().Select( From 19f9a3df8a56143c690a486a6c08b2ea1fbef599 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 22 Apr 2022 10:39:57 -0700 Subject: [PATCH 13/25] Fixing how tests work. --- src/ApiService/Tests/OrmModelsTest.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ApiService/Tests/OrmModelsTest.cs b/src/ApiService/Tests/OrmModelsTest.cs index 8b633568d6..865fe1b4f2 100644 --- a/src/ApiService/Tests/OrmModelsTest.cs +++ b/src/ApiService/Tests/OrmModelsTest.cs @@ -375,6 +375,11 @@ public static Arbitrary Webhook() return Arb.From(OrmGenerators.Webhook()); } + public static Arbitrary WebhookMessageEventGrid() + { + return Arb.From(OrmGenerators.WebhookMessageEventGrid()); + } + public static Arbitrary WebhookMessage() { return Arb.From(OrmGenerators.WebhookMessage()); @@ -579,6 +584,12 @@ public bool Webhook(Webhook wh) return Test(wh); } + [Property] + public bool WebhookMessageEventGrid(WebhookMessageEventGrid grid) + { + return Test(grid); + } + //Sample function on how repro a failing test run, using Replay From 6fd0a586a1c7c47171becc8e34d0c95312ac4dc1 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 25 Apr 2022 14:55:38 -0700 Subject: [PATCH 14/25] Working to resolve conflicts. --- src/ApiService/ApiService/ApiService.csproj | 3 +-- src/ApiService/ApiService/OneFuzzTypes/Model.cs | 4 ++++ src/ApiService/ApiService/onefuzzlib/Creds.cs | 2 +- src/ApiService/ApiService/onefuzzlib/Network.cs | 3 +-- .../ApiService/onefuzzlib/WebhookMessageLogOperations.cs | 2 +- src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs | 4 ++-- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ApiService/ApiService/ApiService.csproj b/src/ApiService/ApiService/ApiService.csproj index 058b4f09a5..d0c948e0e8 100644 --- a/src/ApiService/ApiService/ApiService.csproj +++ b/src/ApiService/ApiService/ApiService.csproj @@ -25,7 +25,6 @@ - @@ -44,4 +43,4 @@ Never - + \ No newline at end of file diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index be37a47ea2..a6b78c31a7 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -318,7 +318,11 @@ Guid[] AllowedGroups public record InstanceConfig ( [PartitionKey, RowKey] string InstanceName, + //# initial set of admins can only be set during deployment. + //# if admins are set, only admins can update instance configs. Guid[]? Admins, + //# if set, only admins can manage pools or scalesets + bool? AllowPoolManagement, string[] AllowedAadTenants, NetworkConfig NetworkConfig, NetworkSecurityGroupConfig ProxyNsgConfig, diff --git a/src/ApiService/ApiService/onefuzzlib/Creds.cs b/src/ApiService/ApiService/onefuzzlib/Creds.cs index 806265a1e8..d93e93cec1 100644 --- a/src/ApiService/ApiService/onefuzzlib/Creds.cs +++ b/src/ApiService/ApiService/onefuzzlib/Creds.cs @@ -67,7 +67,7 @@ public ResourceIdentifier GetResourceGroupResourceIdentifier() public string GetInstanceName() { - var instanceName = EnvironmentVariables.OneFuzz.InstanceName + var instanceName = _config.OneFuzzInstanceName ?? throw new System.Exception("Instance Name env var is not present"); return instanceName; diff --git a/src/ApiService/ApiService/onefuzzlib/Network.cs b/src/ApiService/ApiService/onefuzzlib/Network.cs index 77e9a21e80..a1223e3c10 100644 --- a/src/ApiService/ApiService/onefuzzlib/Network.cs +++ b/src/ApiService/ApiService/onefuzzlib/Network.cs @@ -64,5 +64,4 @@ public static async Async.Task Create(string region, ICreds creds, ICon } -} - +} \ No newline at end of file diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs index 85ec9527dd..580e8f24cb 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs @@ -20,7 +20,7 @@ public class WebhookMessageLogOperations : Orm, IWebhookMessa private readonly ILogTracer _log; private readonly IWebhookOperations _webhook; - public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IWebhookOperations webhook) : base(storage, log) + public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IWebhookOperations webhook, IServiceConfig config) : base(storage, log, config) { _queue = queue; _log = log; diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index 71c618fb89..c65e08c24d 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -23,8 +23,8 @@ public class WebhookOperations : Orm, IWebhookOperations private ICreds _creds; private readonly IContainers _containers; - public WebhookOperations(ICreds creds, IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, IContainers containers, ILogTracer log) - : base(storage, log) + public WebhookOperations(ICreds creds, IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, IContainers containers, ILogTracer log, IServiceConfig config) + : base(storage, log, config) { _webhookMessageLogOperations = webhookMessageLogOperations; _log = log; From 11ded446995d647954bafc7550846094a54b384b Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 25 Apr 2022 14:57:00 -0700 Subject: [PATCH 15/25] Resolving conflicts. --- src/ApiService/ApiService/packages.lock.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ApiService/ApiService/packages.lock.json b/src/ApiService/ApiService/packages.lock.json index 6533d9353f..2e1238ae4d 100644 --- a/src/ApiService/ApiService/packages.lock.json +++ b/src/ApiService/ApiService/packages.lock.json @@ -107,6 +107,18 @@ "System.Text.Json": "4.7.2" } }, + "Azure.Security.KeyVault.Secrets": { + "type": "Direct", + "requested": "[4.3.0, )", + "resolved": "4.3.0", + "contentHash": "GRnmQzTXDVABry1rC8PwuVOHSDCUGn4Om1ABTCzWfHdDSOwRydtQ13ucJ1Z0YtdajklNwxEL6lhHGhFCI0diAw==", + "dependencies": { + "Azure.Core": "1.23.0", + "System.Memory": "4.5.4", + "System.Text.Json": "4.7.2", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, "Azure.Storage.Blobs": { "type": "Direct", "requested": "[12.11.0, )", From 3348ff8984dfc7907eb7f5a7b1cfd7149adef6b7 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 25 Apr 2022 15:34:44 -0700 Subject: [PATCH 16/25] Fixing chagnes. --- .../onefuzzlib/WebhookOperations.cs | 11 +++++--- src/ApiService/Tests/OrmModelsTest.cs | 25 ------------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index c65e08c24d..75266b3895 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -3,6 +3,7 @@ using System.Text.Json; using System.Security.Cryptography; using System.Net; +using System.Net.Http; namespace Microsoft.OneFuzz.Service; @@ -20,16 +21,18 @@ public class WebhookOperations : Orm, IWebhookOperations const string USER_AGENT = "onefuzz-webhook 0.0.0"; private readonly IWebhookMessageLogOperations _webhookMessageLogOperations; private readonly ILogTracer _log; - private ICreds _creds; + private readonly ICreds _creds; private readonly IContainers _containers; + private readonly IHttpClientFactory _httpFactory; - public WebhookOperations(ICreds creds, IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, IContainers containers, ILogTracer log, IServiceConfig config) + public WebhookOperations(IHttpClientFactory httpFactory, ICreds creds, IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, IContainers containers, ILogTracer log, IServiceConfig config) : base(storage, log, config) { _webhookMessageLogOperations = webhookMessageLogOperations; _log = log; _creds = creds; _containers = containers; + _httpFactory = httpFactory; } async public Async.Task SendEvent(EventMessage eventMessage) @@ -80,7 +83,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) headers["X-Onefuzz-Digest"] = digest; } - var client = new Request(); + var client = new Request(_httpFactory.CreateClient()); var response = client.Post(url: webhook.Url, json: data, headers: headers); var result = response.Result; if (result.StatusCode == HttpStatusCode.Accepted) @@ -97,7 +100,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) string data = ""; if (messageFormat != null && messageFormat == WebhookMessageFormat.EventGrid) { - var eventGridMessage = new[] { new WebhookMessageEventGrid(Id: eventId, data: webhookEvent, DataVersion: "1.0.0", Subject: _creds.GetInstanceName(), EventType: eventType, EventTime: DateTimeOffset.UtcNow) }; + var eventGridMessage = new[] { new WebhookMessageEventGrid(Id: eventId, Data: webhookEvent, DataVersion: "1.0.0", Subject: _creds.GetInstanceName(), EventType: eventType, EventTime: DateTimeOffset.UtcNow) }; data = JsonSerializer.Serialize(eventGridMessage, options: EntityConverter.GetJsonSerializerOptions()); } else diff --git a/src/ApiService/Tests/OrmModelsTest.cs b/src/ApiService/Tests/OrmModelsTest.cs index 8f348dc1bf..78b45e29e2 100644 --- a/src/ApiService/Tests/OrmModelsTest.cs +++ b/src/ApiService/Tests/OrmModelsTest.cs @@ -261,22 +261,6 @@ public static Gen WebhookMessageEventGrid() ); ; } - public static Gen WebhookMessageEventGrid() - { - return Arb.Generate>().Select( - arg => - new WebhookMessageEventGrid( - DataVersion: arg.Item1, - Subject: arg.Item1, - EventType: arg.Item2, - EventTime: arg.Item3, - Id: arg.Item4, - data: arg.Item5 - ) - ); - - } - public static Gen Report() { return Arb.Generate, Guid, int>>().Select( @@ -417,10 +401,6 @@ public static Arbitrary Notification() return Arb.From(OrmGenerators.Notification()); } - public static Arbitrary WebhookMessageEventGrid() - { - return Arb.From(OrmGenerators.WebhookMessageEventGrid()); - } } @@ -609,11 +589,6 @@ public bool Webhook(Webhook wh) return Test(wh); } - [Property] - public bool WebhookMessageEventGrid(WebhookMessageEventGrid grid) - { - return Test(grid); - } [Property] public bool Notification(Notification n) From f8d8caa3d044af2a1397ac16d123408dba719024 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 26 Apr 2022 10:25:59 -0700 Subject: [PATCH 17/25] Tested code. --- src/ApiService/ApiService/HttpClient.cs | 6 +- src/ApiService/ApiService/Log.cs | 2 +- .../ApiService/OneFuzzTypes/Events.cs | 12 +- .../ApiService/OneFuzzTypes/Webhooks.cs | 13 +- .../onefuzzlib/WebhookMessageLogOperations.cs | 332 +++++++++--------- .../onefuzzlib/WebhookOperations.cs | 170 ++++++++- 6 files changed, 348 insertions(+), 187 deletions(-) diff --git a/src/ApiService/ApiService/HttpClient.cs b/src/ApiService/ApiService/HttpClient.cs index 6b244ff678..9666b93c54 100644 --- a/src/ApiService/ApiService/HttpClient.cs +++ b/src/ApiService/ApiService/HttpClient.cs @@ -57,20 +57,20 @@ public async Task Post(Uri url, String json, IDictionary Put(Uri url, String json, IDictionary? headers = null) { using var b = new StringContent(json); b.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); - return await Send(method: HttpMethod.Put, url: url, headers: headers); + return await Send(method: HttpMethod.Put, content: b, url: url, headers: headers); } public async Task Patch(Uri url, String json, IDictionary? headers = null) { using var b = new StringContent(json); b.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); - return await Send(method: HttpMethod.Patch, url: url, headers: headers); + return await Send(method: HttpMethod.Patch, content: b, url: url, headers: headers); } } diff --git a/src/ApiService/ApiService/Log.cs b/src/ApiService/ApiService/Log.cs index 12d07d0c35..4568b82746 100644 --- a/src/ApiService/ApiService/Log.cs +++ b/src/ApiService/ApiService/Log.cs @@ -129,7 +129,7 @@ public interface ILogTracer void Critical(string message); void Error(string message); void Event(string evt, IReadOnlyDictionary? metrics); - void Exception(Exception ex, IReadOnlyDictionary? metrics); + void Exception(Exception ex, IReadOnlyDictionary? metrics = null); void ForceFlush(); void Info(string message); void Warning(string message); diff --git a/src/ApiService/ApiService/OneFuzzTypes/Events.cs b/src/ApiService/ApiService/OneFuzzTypes/Events.cs index 205b875826..5af2a2f321 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Events.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Events.cs @@ -49,6 +49,7 @@ public EventType GetEventType() { EventNodeHeartbeat _ => EventType.NodeHeartbeat, EventTaskHeartbeat _ => EventType.TaskHeartbeat, + EventPing _ => EventType.Ping, EventInstanceConfigUpdated _ => EventType.InstanceConfigUpdated, EventProxyCreated _ => EventType.ProxyCreated, EventProxyDeleted _ => EventType.ProxyDeleted, @@ -69,6 +70,7 @@ public static Type GetTypeInfo(EventType eventType) EventType.NodeHeartbeat => typeof(EventNodeHeartbeat), EventType.InstanceConfigUpdated => typeof(EventInstanceConfigUpdated), EventType.TaskHeartbeat => typeof(EventTaskHeartbeat), + EventType.Ping => typeof(EventPing), EventType.ProxyCreated => typeof(EventProxyCreated), EventType.ProxyDeleted => typeof(EventProxyDeleted), EventType.ProxyFailed => typeof(EventProxyFailed), @@ -151,11 +153,9 @@ public record EventTaskHeartbeat( TaskConfig Config ) : BaseEvent(); - -//record EventPing( -// PingId: Guid -//): BaseEvent(); - +public record EventPing( + Guid PingId +) : BaseEvent(); //record EventScalesetCreated( // Guid ScalesetId, @@ -295,7 +295,7 @@ public record EventMessage( BaseEvent Event, Guid InstanceId, String InstanceName -) : EntityBase(); +); public class BaseEventConverter : JsonConverter { diff --git a/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs b/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs index c0d326b9e6..a3b4e105a7 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Webhooks.cs @@ -36,18 +36,15 @@ public record WebhookMessageEventGrid( public record WebhookMessageLog( [RowKey] Guid EventId, EventType EventType, + [property: TypeDiscrimnatorAttribute("EventType", typeof(EventTypeProvider))] + [property: JsonConverter(typeof(BaseEventConverter))] BaseEvent Event, Guid InstanceId, String InstanceName, [PartitionKey] Guid WebhookId, - WebhookMessageState State = WebhookMessageState.Queued, - int TryCount = 0 - ) : WebhookMessage(EventId, - EventType, - Event, - InstanceId, - InstanceName, - WebhookId); + long TryCount, + WebhookMessageState State = WebhookMessageState.Queued + ) : EntityBase(); public record Webhook( [PartitionKey] Guid WebhookId, diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs index 580e8f24cb..2ccaf94a64 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs @@ -1,166 +1,166 @@ -using ApiService.OneFuzzLib.Orm; -using System.Threading.Tasks; - -namespace Microsoft.OneFuzz.Service; - - -public interface IWebhookMessageLogOperations : IOrm -{ - IAsyncEnumerable SearchExpired(); - public Async.Task ProcessFromQueue(WebhookMessageQueueObj obj); -} - - -public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations -{ - const int EXPIRE_DAYS = 7; - const int MAX_TRIES = 5; - - private readonly IQueue _queue; - private readonly ILogTracer _log; - private readonly IWebhookOperations _webhook; - - public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IWebhookOperations webhook, IServiceConfig config) : base(storage, log, config) - { - _queue = queue; - _log = log; - _webhook = webhook; - } - - - public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) - { - var obj = new WebhookMessageQueueObj(webhookLog.WebhookId, webhookLog.EventId); - - TimeSpan? visibilityTimeout = webhookLog.State switch - { - WebhookMessageState.Queued => TimeSpan.Zero, - WebhookMessageState.Retrying => TimeSpan.FromSeconds(30), - _ => null - }; - - if (visibilityTimeout == null) - { - _log.WithTags( - new[] { - ("WebhookId", webhookLog.WebhookId.ToString()), - ("EventId", webhookLog.EventId.ToString()) } - ). - Error($"invalid WebhookMessage queue state, not queuing. {webhookLog.WebhookId}:{webhookLog.EventId} - {webhookLog.State}"); - } - else - { - await _queue.QueueObject("webhooks", obj, StorageType.Config, visibilityTimeout: visibilityTimeout); - } - } - - public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) - { - var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); - - if (message == null) - { - _log.WithTags( - new[] { - ("WebhookId", obj.WebhookId.ToString()), - ("EventId", obj.EventId.ToString()) } - ). - Error($"webhook message log not found for webhookId: {obj.WebhookId} and eventId: {obj.EventId}"); - } - else - { - await Process(message); - } - } - - private async System.Threading.Tasks.Task Process(WebhookMessageLog message) - { - - if (message.State == WebhookMessageState.Failed || message.State == WebhookMessageState.Succeeded) - { - _log.WithTags( - new[] { - ("WebhookId", message.WebhookId.ToString()), - ("EventId", message.EventId.ToString()) } - ). - Error($"webhook message already handled. {message.WebhookId}:{message.EventId}"); - return; - } - - var newMessage = message with { TryCount = message.TryCount + 1 }; - - _log.Info($"sending webhook: {message.WebhookId}:{message.EventId}"); - var success = await Send(newMessage); - if (success) - { - newMessage = newMessage with { State = WebhookMessageState.Succeeded }; - await Replace(newMessage); - _log.Info($"sent webhook event {newMessage.WebhookId}:{newMessage.EventId}"); - } - else if (newMessage.TryCount < MAX_TRIES) - { - newMessage = newMessage with { State = WebhookMessageState.Retrying }; - await Replace(newMessage); - await QueueWebhook(newMessage); - _log.Warning($"sending webhook event failed, re-queued {newMessage.WebhookId}:{newMessage.EventId}"); - } - else - { - newMessage = newMessage with { State = WebhookMessageState.Failed }; - await Replace(newMessage); - _log.Info($"sending webhook: {newMessage.WebhookId} event: {newMessage.EventId} failed {newMessage.TryCount} times."); - } - - } - - private async Task Send(WebhookMessageLog message) - { - var webhook = await _webhook.GetByWebhookId(message.WebhookId); - if (webhook == null) - { - _log.WithTags( - new[] { - ("WebhookId", message.WebhookId.ToString()), - } - ). - Error($"webhook not found for webhookId: {message.WebhookId}"); - return false; - } - - try - { - return await _webhook.Send(message); - } - catch (Exception) - { - _log.WithTags( - new[] { - ("WebhookId", message.WebhookId.ToString()) - } - ). - Error($"webhook send failed. {message.WebhookId}"); - return false; - } - - } - - private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) - { - throw new NotImplementedException(); - } - - public IAsyncEnumerable SearchExpired() - { - var expireTime = (DateTimeOffset.UtcNow - TimeSpan.FromDays(EXPIRE_DAYS)).ToString("o"); - - var timeFilter = $"Timestamp lt datetime'{expireTime}'"; - return QueryAsync(filter: timeFilter); - } - - public async Async.Task GetWebhookMessageById(Guid webhookId, Guid eventId) - { - var data = QueryAsync(filter: $"PartitionKey eq '{webhookId}' and Rowkey eq '{eventId}'"); - - return await data.FirstOrDefaultAsync(); - } -} +// using ApiService.OneFuzzLib.Orm; +// using System.Threading.Tasks; + +// namespace Microsoft.OneFuzz.Service; + + +// public interface IWebhookMessageLogOperations : IOrm +// { +// IAsyncEnumerable SearchExpired(); +// public Async.Task ProcessFromQueue(WebhookMessageQueueObj obj); +// } + + +// public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations +// { +// const int EXPIRE_DAYS = 7; +// const int MAX_TRIES = 5; + +// private readonly IQueue _queue; +// private readonly ILogTracer _log; +// private readonly IWebhookOperations _webhook; + +// public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IWebhookOperations webhook, IServiceConfig config) : base(storage, log, config) +// { +// _queue = queue; +// _log = log; +// _webhook = webhook; +// } + + +// public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) +// { +// var obj = new WebhookMessageQueueObj(webhookLog.WebhookId, webhookLog.EventId); + +// TimeSpan? visibilityTimeout = webhookLog.State switch +// { +// WebhookMessageState.Queued => TimeSpan.Zero, +// WebhookMessageState.Retrying => TimeSpan.FromSeconds(30), +// _ => null +// }; + +// if (visibilityTimeout == null) +// { +// _log.WithTags( +// new[] { +// ("WebhookId", webhookLog.WebhookId.ToString()), +// ("EventId", webhookLog.EventId.ToString()) } +// ). +// Error($"invalid WebhookMessage queue state, not queuing. {webhookLog.WebhookId}:{webhookLog.EventId} - {webhookLog.State}"); +// } +// else +// { +// await _queue.QueueObject("webhooks", obj, StorageType.Config, visibilityTimeout: visibilityTimeout); +// } +// } + +// public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) +// { +// var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); + +// if (message == null) +// { +// _log.WithTags( +// new[] { +// ("WebhookId", obj.WebhookId.ToString()), +// ("EventId", obj.EventId.ToString()) } +// ). +// Error($"webhook message log not found for webhookId: {obj.WebhookId} and eventId: {obj.EventId}"); +// } +// else +// { +// await Process(message); +// } +// } + +// private async System.Threading.Tasks.Task Process(WebhookMessageLog message) +// { + +// if (message.State == WebhookMessageState.Failed || message.State == WebhookMessageState.Succeeded) +// { +// _log.WithTags( +// new[] { +// ("WebhookId", message.WebhookId.ToString()), +// ("EventId", message.EventId.ToString()) } +// ). +// Error($"webhook message already handled. {message.WebhookId}:{message.EventId}"); +// return; +// } + +// var newMessage = message with { TryCount = message.TryCount + 1 }; + +// _log.Info($"sending webhook: {message.WebhookId}:{message.EventId}"); +// var success = await Send(newMessage); +// if (success) +// { +// newMessage = newMessage with { State = WebhookMessageState.Succeeded }; +// await Replace(newMessage); +// _log.Info($"sent webhook event {newMessage.WebhookId}:{newMessage.EventId}"); +// } +// else if (newMessage.TryCount < MAX_TRIES) +// { +// newMessage = newMessage with { State = WebhookMessageState.Retrying }; +// await Replace(newMessage); +// await QueueWebhook(newMessage); +// _log.Warning($"sending webhook event failed, re-queued {newMessage.WebhookId}:{newMessage.EventId}"); +// } +// else +// { +// newMessage = newMessage with { State = WebhookMessageState.Failed }; +// await Replace(newMessage); +// _log.Info($"sending webhook: {newMessage.WebhookId} event: {newMessage.EventId} failed {newMessage.TryCount} times."); +// } + +// } + +// private async Task Send(WebhookMessageLog message) +// { +// var webhook = await _webhook.GetByWebhookId(message.WebhookId); +// if (webhook == null) +// { +// _log.WithTags( +// new[] { +// ("WebhookId", message.WebhookId.ToString()), +// } +// ). +// Error($"webhook not found for webhookId: {message.WebhookId}"); +// return false; +// } + +// try +// { +// return await _webhook.Send(message); +// } +// catch (Exception) +// { +// _log.WithTags( +// new[] { +// ("WebhookId", message.WebhookId.ToString()) +// } +// ). +// Error($"webhook send failed. {message.WebhookId}"); +// return false; +// } + +// } + +// private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) +// { +// throw new NotImplementedException(); +// } + +// public IAsyncEnumerable SearchExpired() +// { +// var expireTime = (DateTimeOffset.UtcNow - TimeSpan.FromDays(EXPIRE_DAYS)).ToString("o"); + +// var timeFilter = $"Timestamp lt datetime'{expireTime}'"; +// return QueryAsync(filter: timeFilter); +// } + +// public async Async.Task GetWebhookMessageById(Guid webhookId, Guid eventId) +// { +// var data = QueryAsync(filter: $"PartitionKey eq '{webhookId}' and Rowkey eq '{eventId}'"); + +// return await data.FirstOrDefaultAsync(); +// } +// } diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index 75266b3895..588c03d4d5 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -55,7 +55,8 @@ async private Async.Task AddEvent(Webhook webhook, EventMessage eventMessage) Event: eventMessage.Event, InstanceId: eventMessage.InstanceId, InstanceName: eventMessage.InstanceName, - WebhookId: webhook.WebhookId + WebhookId: webhook.WebhookId, + TryCount: 0 ); var r = await _webhookMessageLogOperations.Replace(message); @@ -76,7 +77,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) var (data, digest) = await BuildMessage(webhookId: webhook.WebhookId, eventId: messageLog.EventId, eventType: messageLog.EventType, webhookEvent: messageLog.Event, secretToken: webhook.SecretToken, messageFormat: webhook.MessageFormat); - var headers = new Dictionary { { "Content-type", "application/json" }, { "User-Agent", USER_AGENT } }; + var headers = new Dictionary { { "User-Agent", USER_AGENT }}; if (digest != null) { @@ -84,6 +85,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) } var client = new Request(_httpFactory.CreateClient()); + _log.Info(data); var response = client.Post(url: webhook.Url, json: data, headers: headers); var result = response.Result; if (result.StatusCode == HttpStatusCode.Accepted) @@ -107,6 +109,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) { var instanceId = await _containers.GetInstanceId(); var webhookMessage = new WebhookMessage(WebhookId: webhookId, EventId: eventId, EventType: eventType, Event: webhookEvent, InstanceId: instanceId, InstanceName: _creds.GetInstanceName()); + data = JsonSerializer.Serialize(webhookMessage, options: EntityConverter.GetJsonSerializerOptions()); } @@ -117,7 +120,6 @@ public async Async.Task Send(WebhookMessageLog messageLog) hmac.Key = System.Text.Encoding.UTF8.GetBytes(secretToken); digest = Convert.ToHexString(hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(data))); } - return new Tuple(data, digest); } @@ -136,3 +138,165 @@ public IAsyncEnumerable GetWebhooksCached() } } + +public interface IWebhookMessageLogOperations : IOrm +{ + IAsyncEnumerable SearchExpired(); + public Async.Task ProcessFromQueue(WebhookMessageQueueObj obj); +} + + +public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations +{ + const int EXPIRE_DAYS = 7; + const int MAX_TRIES = 5; + + private readonly IQueue _queue; + private readonly ILogTracer _log; + private readonly IWebhookOperations _webhook; + + public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IServiceConfig config, ICreds creds, IHttpClientFactory httpFactory, IContainers containers) : base(storage, log, config) + { + _queue = queue; + _log = log; + _webhook = new WebhookOperations(httpFactory: httpFactory, creds: creds, storage: storage, webhookMessageLogOperations: this, containers: containers, log: log, config: config); + } + + + public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) + { + var obj = new WebhookMessageQueueObj(webhookLog.WebhookId, webhookLog.EventId); + + TimeSpan? visibilityTimeout = webhookLog.State switch + { + WebhookMessageState.Queued => TimeSpan.Zero, + WebhookMessageState.Retrying => TimeSpan.FromSeconds(30), + _ => null + }; + + if (visibilityTimeout == null) + { + _log.WithTags( + new[] { + ("WebhookId", webhookLog.WebhookId.ToString()), + ("EventId", webhookLog.EventId.ToString()) } + ). + Error($"invalid WebhookMessage queue state, not queuing. {webhookLog.WebhookId}:{webhookLog.EventId} - {webhookLog.State}"); + } + else + { + await _queue.QueueObject("webhooks", obj, StorageType.Config, visibilityTimeout: visibilityTimeout); + } + } + + public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) + { + var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); + + if (message == null) + { + _log.WithTags( + new[] { + ("WebhookId", obj.WebhookId.ToString()), + ("EventId", obj.EventId.ToString()) } + ). + Error($"webhook message log not found for webhookId: {obj.WebhookId} and eventId: {obj.EventId}"); + } + else + { + await Process(message); + } + } + + private async System.Threading.Tasks.Task Process(WebhookMessageLog message) + { + + if (message.State == WebhookMessageState.Failed || message.State == WebhookMessageState.Succeeded) + { + _log.WithTags( + new[] { + ("WebhookId", message.WebhookId.ToString()), + ("EventId", message.EventId.ToString()) } + ). + Error($"webhook message already handled. {message.WebhookId}:{message.EventId}"); + return; + } + + var newMessage = message with { TryCount = message.TryCount + 1 }; + + _log.Info($"sending webhook: {message.WebhookId}:{message.EventId}"); + var success = await Send(newMessage); + if (success) + { + newMessage = newMessage with { State = WebhookMessageState.Succeeded }; + await Replace(newMessage); + _log.Info($"sent webhook event {newMessage.WebhookId}:{newMessage.EventId}"); + } + else if (newMessage.TryCount < MAX_TRIES) + { + newMessage = newMessage with { State = WebhookMessageState.Retrying }; + await Replace(newMessage); + await QueueWebhook(newMessage); + _log.Warning($"sending webhook event failed, re-queued {newMessage.WebhookId}:{newMessage.EventId}"); + } + else + { + newMessage = newMessage with { State = WebhookMessageState.Failed }; + await Replace(newMessage); + _log.Info($"sending webhook: {newMessage.WebhookId} event: {newMessage.EventId} failed {newMessage.TryCount} times."); + } + + } + + private async Async.Task Send(WebhookMessageLog message) + { + var webhook = await _webhook.GetByWebhookId(message.WebhookId); + if (webhook == null) + { + _log.WithTags( + new[] { + ("WebhookId", message.WebhookId.ToString()), + } + ). + Error($"webhook not found for webhookId: {message.WebhookId}"); + return false; + } + + try + { + return await _webhook.Send(message); + } + catch (Exception exc) + { + _log.WithTags( + new[] { + ("WebhookId", message.WebhookId.ToString()) + } + ). + Exception(exc); + return false; + } + + } + + private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) + { + throw new NotImplementedException(); + } + + public IAsyncEnumerable SearchExpired() + { + var expireTime = (DateTimeOffset.UtcNow - TimeSpan.FromDays(EXPIRE_DAYS)).ToString("o"); + + var timeFilter = $"Timestamp lt datetime'{expireTime}'"; + return QueryAsync(filter: timeFilter); + } + + public async Async.Task GetWebhookMessageById(Guid webhookId, Guid eventId) + { + var data = QueryAsync(filter: $"PartitionKey eq '{webhookId}' and RowKey eq '{eventId}'"); + + return await data.FirstOrDefaultAsync(); + } +} + From ddb3373664cc90c2093ff809b909fb393965ee78 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 26 Apr 2022 10:32:37 -0700 Subject: [PATCH 18/25] Formatting. --- .../ApiService/OneFuzzTypes/Model.cs | 18 +++++++++--------- src/ApiService/ApiService/Program.cs | 8 ++++---- .../ApiService/onefuzzlib/WebhookOperations.cs | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index b3dbd153b2..d820bffaaa 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -317,11 +317,11 @@ Guid[] AllowedGroups //# if set, only admins can manage pools or scalesets public record InstanceConfig ( - [PartitionKey, RowKey] string InstanceName, - //# initial set of admins can only be set during deployment. - //# if admins are set, only admins can update instance configs. - Guid[]? Admins, - //# if set, only admins can manage pools or scalesets + [PartitionKey, RowKey] string InstanceName, + //# initial set of admins can only be set during deployment. + //# if admins are set, only admins can update instance configs. + Guid[]? Admins, + //# if set, only admins can manage pools or scalesets bool? AllowPoolManagement, string[] AllowedAadTenants, NetworkConfig NetworkConfig, @@ -515,8 +515,8 @@ public record Repro( public record ReproConfig( Container Container, - string Path, - // TODO: Make this >1 and < 7*24 (more than one hour, less than seven days) + string Path, + // TODO: Make this >1 and < 7*24 (more than one hour, less than seven days) int Duration ); @@ -525,8 +525,8 @@ public record Pool( PoolName Name, Guid PoolId, Os Os, - bool Managed, - // Skipping AutoScaleConfig because it's not used anymore + bool Managed, + // Skipping AutoScaleConfig because it's not used anymore Architecture Architecture, PoolState State, Guid? ClientId, diff --git a/src/ApiService/ApiService/Program.cs b/src/ApiService/ApiService/Program.cs index 751cf0737b..b94e59eb63 100644 --- a/src/ApiService/ApiService/Program.cs +++ b/src/ApiService/ApiService/Program.cs @@ -88,10 +88,10 @@ public static void Main() .AddScoped() .AddScoped() .AddScoped() - .AddScoped() - - //Move out expensive resources into separate class, and add those as Singleton - // ArmClient, Table Client(s), Queue Client(s), HttpClient, etc. + .AddScoped() + + //Move out expensive resources into separate class, and add those as Singleton + // ArmClient, Table Client(s), Queue Client(s), HttpClient, etc. .AddSingleton() .AddSingleton() .AddHttpClient() diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index 588c03d4d5..90bd89cfb1 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -77,7 +77,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) var (data, digest) = await BuildMessage(webhookId: webhook.WebhookId, eventId: messageLog.EventId, eventType: messageLog.EventType, webhookEvent: messageLog.Event, secretToken: webhook.SecretToken, messageFormat: webhook.MessageFormat); - var headers = new Dictionary { { "User-Agent", USER_AGENT }}; + var headers = new Dictionary { { "User-Agent", USER_AGENT } }; if (digest != null) { @@ -109,7 +109,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) { var instanceId = await _containers.GetInstanceId(); var webhookMessage = new WebhookMessage(WebhookId: webhookId, EventId: eventId, EventType: eventType, Event: webhookEvent, InstanceId: instanceId, InstanceName: _creds.GetInstanceName()); - + data = JsonSerializer.Serialize(webhookMessage, options: EntityConverter.GetJsonSerializerOptions()); } From c79c59d649bcf00bc3b76f24b7e47f54cb765bc3 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 26 Apr 2022 10:33:42 -0700 Subject: [PATCH 19/25] MoreFormatting. --- src/ApiService/ApiService/QueueWebhooks.cs | 13 +- .../ApiService/onefuzzlib/Containers.cs | 2 +- src/ApiService/ApiService/onefuzzlib/Creds.cs | 6 +- .../ApiService/onefuzzlib/Network.cs | 2 +- .../ApiService/onefuzzlib/Storage.cs | 24 ++-- .../onefuzzlib/WebhookOperations.cs | 136 ++++++------------ 6 files changed, 64 insertions(+), 119 deletions(-) diff --git a/src/ApiService/ApiService/QueueWebhooks.cs b/src/ApiService/ApiService/QueueWebhooks.cs index 99d6e7e543..c5cd0fa3cf 100644 --- a/src/ApiService/ApiService/QueueWebhooks.cs +++ b/src/ApiService/ApiService/QueueWebhooks.cs @@ -1,22 +1,19 @@ +using System.Text.Json; using Microsoft.Azure.Functions.Worker; -using System.Text.Json; using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; namespace Microsoft.OneFuzz.Service; -public class QueueWebhooks -{ +public class QueueWebhooks { private readonly ILogTracer _log; private readonly IWebhookMessageLogOperations _webhookMessageLog; - public QueueWebhooks(ILogTracer log, IWebhookMessageLogOperations webhookMessageLog) - { + public QueueWebhooks(ILogTracer log, IWebhookMessageLogOperations webhookMessageLog) { _log = log; _webhookMessageLog = webhookMessageLog; } [Function("QueueWebhooks")] - public async Async.Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")] string msg) - { + public async Async.Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")] string msg) { _log.Info($"Webhook Message Queued: {msg}"); @@ -24,4 +21,4 @@ public async Async.Task Run([QueueTrigger("myqueue-items", Connection = "AzureWe await _webhookMessageLog.ProcessFromQueue(obj); } -} \ No newline at end of file +} diff --git a/src/ApiService/ApiService/onefuzzlib/Containers.cs b/src/ApiService/ApiService/onefuzzlib/Containers.cs index 7abc88dfae..1c271a9af6 100644 --- a/src/ApiService/ApiService/onefuzzlib/Containers.cs +++ b/src/ApiService/ApiService/onefuzzlib/Containers.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Azure; using Azure.ResourceManager; using Azure.Storage; diff --git a/src/ApiService/ApiService/onefuzzlib/Creds.cs b/src/ApiService/ApiService/onefuzzlib/Creds.cs index fbaad05a91..8e2b8c871b 100644 --- a/src/ApiService/ApiService/onefuzzlib/Creds.cs +++ b/src/ApiService/ApiService/onefuzzlib/Creds.cs @@ -60,16 +60,14 @@ public ResourceIdentifier GetResourceGroupResourceIdentifier() { return new ResourceIdentifier(resourceId); } - public string GetInstanceName() - { + public string GetInstanceName() { var instanceName = _config.OneFuzzInstanceName ?? throw new System.Exception("Instance Name env var is not present"); return instanceName; } - public ResourceGroupResource GetResourceGroupResource() - { + public ResourceGroupResource GetResourceGroupResource() { var resourceId = GetResourceGroupResourceIdentifier(); return ArmClient.GetResourceGroupResource(resourceId); } diff --git a/src/ApiService/ApiService/onefuzzlib/Network.cs b/src/ApiService/ApiService/onefuzzlib/Network.cs index 51d80dbbfe..0adf20cd73 100644 --- a/src/ApiService/ApiService/onefuzzlib/Network.cs +++ b/src/ApiService/ApiService/onefuzzlib/Network.cs @@ -55,4 +55,4 @@ public static async Async.Task Create(string region, ICreds creds, ICon } -} \ No newline at end of file +} diff --git a/src/ApiService/ApiService/onefuzzlib/Storage.cs b/src/ApiService/ApiService/onefuzzlib/Storage.cs index e07ec7666c..926dbbe01f 100644 --- a/src/ApiService/ApiService/onefuzzlib/Storage.cs +++ b/src/ApiService/ApiService/onefuzzlib/Storage.cs @@ -97,17 +97,14 @@ public string GetPrimaryAccount(StorageType storageType) { return (resourceId.Name, key?.Value); } - public string ChooseAccounts(StorageType storageType) - { + public string ChooseAccounts(StorageType storageType) { var accounts = GetAccounts(storageType); - if (!accounts.Any()) - { + if (!accounts.Any()) { throw new Exception($"No Storage Accounts for {storageType}"); } var account_list = accounts.ToList(); - if (account_list.Count == 1) - { + if (account_list.Count == 1) { return account_list[0]; } @@ -122,15 +119,14 @@ public string ChooseAccounts(StorageType storageType) return account_list[index]; // nosec } - public IEnumerable GetAccounts(StorageType storageType) - { + public IEnumerable GetAccounts(StorageType storageType) { switch (storageType) - case StorageType.Corpus: - return CorpusAccounts(); - case StorageType.Config: - return new[] { GetFuncStorage() }; - default: - throw new NotImplementedException(); + case StorageType.Corpus: + return CorpusAccounts(); + case StorageType.Config: + return new[] { GetFuncStorage() }; + default: + throw new NotImplementedException(); } } } diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index d945f4e672..9376ba0a36 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -1,21 +1,19 @@ -using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; -using ApiService.OneFuzzLib.Orm; -using System.Text.Json; -using System.Security.Cryptography; -using System.Net; +using System.Net; using System.Net.Http; +using System.Security.Cryptography; +using System.Text.Json; +using ApiService.OneFuzzLib.Orm; +using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; namespace Microsoft.OneFuzz.Service; -public interface IWebhookOperations -{ +public interface IWebhookOperations { Async.Task SendEvent(EventMessage eventMessage); Async.Task GetByWebhookId(Guid webhookId); Async.Task Send(WebhookMessageLog messageLog); } -public class WebhookOperations : Orm, IWebhookOperations -{ +public class WebhookOperations : Orm, IWebhookOperations { // Needs to eventually pull from global __version__ const string USER_AGENT = "onefuzz-webhook 0.0.0"; @@ -26,8 +24,7 @@ public class WebhookOperations : Orm, IWebhookOperations private readonly IHttpClientFactory _httpFactory; public WebhookOperations(IHttpClientFactory httpFactory, ICreds creds, IStorage storage, IWebhookMessageLogOperations webhookMessageLogOperations, IContainers containers, ILogTracer log, IServiceConfig config) - : base(storage, log, config) - { + : base(storage, log, config) { _webhookMessageLogOperations = webhookMessageLogOperations; _log = log; _creds = creds; @@ -35,20 +32,16 @@ public WebhookOperations(IHttpClientFactory httpFactory, ICreds creds, IStorage _httpFactory = httpFactory; } - async public Async.Task SendEvent(EventMessage eventMessage) - { - await foreach (var webhook in GetWebhooksCached()) - { - if (!webhook.EventTypes.Contains(eventMessage.EventType)) - { + async public Async.Task SendEvent(EventMessage eventMessage) { + await foreach (var webhook in GetWebhooksCached()) { + if (!webhook.EventTypes.Contains(eventMessage.EventType)) { continue; } await AddEvent(webhook, eventMessage); } } - async private Async.Task AddEvent(Webhook webhook, EventMessage eventMessage) - { + async private Async.Task AddEvent(Webhook webhook, EventMessage eventMessage) { var message = new WebhookMessageLog( EventId: eventMessage.EventId, EventType: eventMessage.EventType, @@ -60,18 +53,15 @@ async private Async.Task AddEvent(Webhook webhook, EventMessage eventMessage) ); var r = await _webhookMessageLogOperations.Replace(message); - if (!r.IsOk) - { + if (!r.IsOk) { var (status, reason) = r.ErrorV; _log.Error($"Failed to replace webhook message log due to [{status}] {reason}"); } } - public async Async.Task Send(WebhookMessageLog messageLog) - { + public async Async.Task Send(WebhookMessageLog messageLog) { var webhook = await GetByWebhookId(messageLog.WebhookId); - if (webhook == null || webhook.Url == null) - { + if (webhook == null || webhook.Url == null) { throw new Exception($"Invalid Webhook. Webhook with WebhookId: {messageLog.WebhookId} Not Found"); } @@ -79,8 +69,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) var headers = new Dictionary { { "User-Agent", USER_AGENT } }; - if (digest != null) - { + if (digest != null) { headers["X-Onefuzz-Digest"] = digest; } @@ -88,25 +77,20 @@ public async Async.Task Send(WebhookMessageLog messageLog) _log.Info(data); var response = client.Post(url: webhook.Url, json: data, headers: headers); var result = response.Result; - if (result.StatusCode == HttpStatusCode.Accepted) - { + if (result.StatusCode == HttpStatusCode.Accepted) { return true; } return false; } // Not converting to bytes, as it's not neccessary in C#. Just keeping as string. - public async Async.Task> BuildMessage(Guid webhookId, Guid eventId, EventType eventType, BaseEvent webhookEvent, String? secretToken, WebhookMessageFormat? messageFormat) - { + public async Async.Task> BuildMessage(Guid webhookId, Guid eventId, EventType eventType, BaseEvent webhookEvent, String? secretToken, WebhookMessageFormat? messageFormat) { var entityConverter = new EntityConverter(); string data = ""; - if (messageFormat != null && messageFormat == WebhookMessageFormat.EventGrid) - { + if (messageFormat != null && messageFormat == WebhookMessageFormat.EventGrid) { var eventGridMessage = new[] { new WebhookMessageEventGrid(Id: eventId, Data: webhookEvent, DataVersion: "1.0.0", Subject: _creds.GetInstanceName(), EventType: eventType, EventTime: DateTimeOffset.UtcNow) }; data = JsonSerializer.Serialize(eventGridMessage, options: EntityConverter.GetJsonSerializerOptions()); - } - else - { + } else { var instanceId = await _containers.GetInstanceId(); var webhookMessage = new WebhookMessage(WebhookId: webhookId, EventId: eventId, EventType: eventType, Event: webhookEvent, InstanceId: instanceId, InstanceName: _creds.GetInstanceName()); @@ -115,8 +99,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) string? digest = null; var hmac = HMAC.Create("HMACSHA512"); - if (secretToken != null && hmac != null) - { + if (secretToken != null && hmac != null) { hmac.Key = System.Text.Encoding.UTF8.GetBytes(secretToken); digest = Convert.ToHexString(hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(data))); } @@ -124,30 +107,26 @@ public async Async.Task Send(WebhookMessageLog messageLog) } - public async Async.Task GetByWebhookId(Guid webhookId) - { + public async Async.Task GetByWebhookId(Guid webhookId) { var data = QueryAsync(filter: $"PartitionKey eq '{webhookId}'"); return await data.FirstOrDefaultAsync(); } //todo: caching - public IAsyncEnumerable GetWebhooksCached() - { + public IAsyncEnumerable GetWebhooksCached() { return QueryAsync(); } } -public interface IWebhookMessageLogOperations : IOrm -{ +public interface IWebhookMessageLogOperations : IOrm { IAsyncEnumerable SearchExpired(); public Async.Task ProcessFromQueue(WebhookMessageQueueObj obj); } -public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations -{ +public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations { const int EXPIRE_DAYS = 7; const int MAX_TRIES = 5; @@ -155,64 +134,52 @@ public class WebhookMessageLogOperations : Orm, IWebhookMessa private readonly ILogTracer _log; private readonly IWebhookOperations _webhook; - public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IServiceConfig config, ICreds creds, IHttpClientFactory httpFactory, IContainers containers) : base(storage, log, config) - { + public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IServiceConfig config, ICreds creds, IHttpClientFactory httpFactory, IContainers containers) : base(storage, log, config) { _queue = queue; _log = log; _webhook = new WebhookOperations(httpFactory: httpFactory, creds: creds, storage: storage, webhookMessageLogOperations: this, containers: containers, log: log, config: config); } - public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) - { + public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) { var obj = new WebhookMessageQueueObj(webhookLog.WebhookId, webhookLog.EventId); - TimeSpan? visibilityTimeout = webhookLog.State switch - { + TimeSpan? visibilityTimeout = webhookLog.State switch { WebhookMessageState.Queued => TimeSpan.Zero, WebhookMessageState.Retrying => TimeSpan.FromSeconds(30), _ => null }; - if (visibilityTimeout == null) - { + if (visibilityTimeout == null) { _log.WithTags( new[] { ("WebhookId", webhookLog.WebhookId.ToString()), ("EventId", webhookLog.EventId.ToString()) } ). Error($"invalid WebhookMessage queue state, not queuing. {webhookLog.WebhookId}:{webhookLog.EventId} - {webhookLog.State}"); - } - else - { + } else { await _queue.QueueObject("webhooks", obj, StorageType.Config, visibilityTimeout: visibilityTimeout); } } - public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) - { + public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) { var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); - if (message == null) - { + if (message == null) { _log.WithTags( new[] { ("WebhookId", obj.WebhookId.ToString()), ("EventId", obj.EventId.ToString()) } ). Error($"webhook message log not found for webhookId: {obj.WebhookId} and eventId: {obj.EventId}"); - } - else - { + } else { await Process(message); } } - private async System.Threading.Tasks.Task Process(WebhookMessageLog message) - { + private async System.Threading.Tasks.Task Process(WebhookMessageLog message) { - if (message.State == WebhookMessageState.Failed || message.State == WebhookMessageState.Succeeded) - { + if (message.State == WebhookMessageState.Failed || message.State == WebhookMessageState.Succeeded) { _log.WithTags( new[] { ("WebhookId", message.WebhookId.ToString()), @@ -226,21 +193,16 @@ private async System.Threading.Tasks.Task Process(WebhookMessageLog message) _log.Info($"sending webhook: {message.WebhookId}:{message.EventId}"); var success = await Send(newMessage); - if (success) - { + if (success) { newMessage = newMessage with { State = WebhookMessageState.Succeeded }; await Replace(newMessage); _log.Info($"sent webhook event {newMessage.WebhookId}:{newMessage.EventId}"); - } - else if (newMessage.TryCount < MAX_TRIES) - { + } else if (newMessage.TryCount < MAX_TRIES) { newMessage = newMessage with { State = WebhookMessageState.Retrying }; await Replace(newMessage); await QueueWebhook(newMessage); _log.Warning($"sending webhook event failed, re-queued {newMessage.WebhookId}:{newMessage.EventId}"); - } - else - { + } else { newMessage = newMessage with { State = WebhookMessageState.Failed }; await Replace(newMessage); _log.Info($"sending webhook: {newMessage.WebhookId} event: {newMessage.EventId} failed {newMessage.TryCount} times."); @@ -248,11 +210,9 @@ private async System.Threading.Tasks.Task Process(WebhookMessageLog message) } - private async Async.Task Send(WebhookMessageLog message) - { + private async Async.Task Send(WebhookMessageLog message) { var webhook = await _webhook.GetByWebhookId(message.WebhookId); - if (webhook == null) - { + if (webhook == null) { _log.WithTags( new[] { ("WebhookId", message.WebhookId.ToString()), @@ -262,12 +222,9 @@ private async Async.Task Send(WebhookMessageLog message) return false; } - try - { + try { return await _webhook.Send(message); - } - catch (Exception exc) - { + } catch (Exception exc) { _log.WithTags( new[] { ("WebhookId", message.WebhookId.ToString()) @@ -279,21 +236,18 @@ private async Async.Task Send(WebhookMessageLog message) } - private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) - { + private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) { throw new NotImplementedException(); } - public IAsyncEnumerable SearchExpired() - { + public IAsyncEnumerable SearchExpired() { var expireTime = (DateTimeOffset.UtcNow - TimeSpan.FromDays(EXPIRE_DAYS)).ToString("o"); var timeFilter = $"Timestamp lt datetime'{expireTime}'"; return QueryAsync(filter: timeFilter); } - public async Async.Task GetWebhookMessageById(Guid webhookId, Guid eventId) - { + public async Async.Task GetWebhookMessageById(Guid webhookId, Guid eventId) { var data = QueryAsync(filter: $"PartitionKey eq '{webhookId}' and RowKey eq '{eventId}'"); return await data.FirstOrDefaultAsync(); From 79b05d664f7aa6b592d39ba2c88deef72de0ae71 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 26 Apr 2022 10:41:10 -0700 Subject: [PATCH 20/25] More formatting. --- src/ApiService/ApiService/OneFuzzTypes/Model.cs | 15 ++++++--------- src/ApiService/ApiService/Program.cs | 8 +++----- src/ApiService/Tests/OrmModelsTest.cs | 1 - 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index 3ff7bbe961..d90e3a675f 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -311,11 +311,8 @@ Guid[] AllowedGroups //# if set, only admins can manage pools or scalesets public record InstanceConfig ( - [PartitionKey, RowKey] string InstanceName, - //# initial set of admins can only be set during deployment. - //# if admins are set, only admins can update instance configs. - Guid[]? Admins, - //# if set, only admins can manage pools or scalesets + [PartitionKey, RowKey] string InstanceName, + Guid[]? Admins, bool? AllowPoolManagement, string[] AllowedAadTenants, NetworkConfig NetworkConfig, @@ -492,20 +489,20 @@ public record Repro( UserInfo? UserInfo ) : StatefulEntityBase(State); +// TODO: Make this >1 and < 7*24 (more than one hour, less than seven days) public record ReproConfig( Container Container, - string Path, - // TODO: Make this >1 and < 7*24 (more than one hour, less than seven days) + string Path, int Duration ); +// Skipping AutoScaleConfig because it's not used anymore public record Pool( DateTimeOffset Timestamp, PoolName Name, Guid PoolId, Os Os, - bool Managed, - // Skipping AutoScaleConfig because it's not used anymore + bool Managed, Architecture Architecture, PoolState State, Guid? ClientId, diff --git a/src/ApiService/ApiService/Program.cs b/src/ApiService/ApiService/Program.cs index 7343275295..d096340aa2 100644 --- a/src/ApiService/ApiService/Program.cs +++ b/src/ApiService/ApiService/Program.cs @@ -45,7 +45,8 @@ public static List GetLoggers(IServiceConfig config) { return loggers; } - + //Move out expensive resources into separate class, and add those as Singleton + // ArmClient, Table Client(s), Queue Client(s), HttpClient, etc. public static void Main() { var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults( @@ -77,10 +78,7 @@ public static void Main() { .AddScoped() .AddScoped() .AddScoped() - .AddScoped() - - //Move out expensive resources into separate class, and add those as Singleton - // ArmClient, Table Client(s), Queue Client(s), HttpClient, etc. + .AddScoped() .AddSingleton() .AddSingleton() .AddHttpClient() diff --git a/src/ApiService/Tests/OrmModelsTest.cs b/src/ApiService/Tests/OrmModelsTest.cs index 488fe975f3..35070840b8 100644 --- a/src/ApiService/Tests/OrmModelsTest.cs +++ b/src/ApiService/Tests/OrmModelsTest.cs @@ -541,7 +541,6 @@ public bool Webhook(Webhook wh) { return Test(wh); } - [Property] public bool Notification(Notification n) { return Test(n); From 78da0dd6d9a064f8d69b99703f379d990ca814dd Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 26 Apr 2022 10:43:38 -0700 Subject: [PATCH 21/25] Fixing syntax. --- src/ApiService/ApiService/onefuzzlib/Storage.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/Storage.cs b/src/ApiService/ApiService/onefuzzlib/Storage.cs index 926dbbe01f..94fa7eab2c 100644 --- a/src/ApiService/ApiService/onefuzzlib/Storage.cs +++ b/src/ApiService/ApiService/onefuzzlib/Storage.cs @@ -121,11 +121,12 @@ public string ChooseAccounts(StorageType storageType) { public IEnumerable GetAccounts(StorageType storageType) { switch (storageType) - case StorageType.Corpus: - return CorpusAccounts(); - case StorageType.Config: - return new[] { GetFuncStorage() }; - default: + { + case StorageType.Corpus: + return CorpusAccounts(); + case StorageType.Config: + return new[] { GetFuncStorage() }; + default: throw new NotImplementedException(); } } From 1cc3d8aeb34761f8bb1cbc72b7794c20beff4abd Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 26 Apr 2022 10:46:53 -0700 Subject: [PATCH 22/25] Fixing syntax. --- src/ApiService/ApiService/onefuzzlib/Storage.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/Storage.cs b/src/ApiService/ApiService/onefuzzlib/Storage.cs index 94fa7eab2c..6e7b12f339 100644 --- a/src/ApiService/ApiService/onefuzzlib/Storage.cs +++ b/src/ApiService/ApiService/onefuzzlib/Storage.cs @@ -120,14 +120,13 @@ public string ChooseAccounts(StorageType storageType) { } public IEnumerable GetAccounts(StorageType storageType) { - switch (storageType) - { + switch (storageType) { case StorageType.Corpus: - return CorpusAccounts(); - case StorageType.Config: - return new[] { GetFuncStorage() }; - default: - throw new NotImplementedException(); + return CorpusAccounts(); + case StorageType.Config: + return new[] { GetFuncStorage() }; + default: + throw new NotImplementedException(); } } } From 72a39c3f282341e38cf063e2f128f3cb7fe59ad0 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 26 Apr 2022 10:54:59 -0700 Subject: [PATCH 23/25] Removing test and webhookmessagelogoperation class. --- .../onefuzzlib/WebhookMessageLogOperations.cs | 166 ------------------ src/ApiService/Tests/OrmTest.cs | 13 -- 2 files changed, 179 deletions(-) delete mode 100644 src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs deleted file mode 100644 index 2ccaf94a64..0000000000 --- a/src/ApiService/ApiService/onefuzzlib/WebhookMessageLogOperations.cs +++ /dev/null @@ -1,166 +0,0 @@ -// using ApiService.OneFuzzLib.Orm; -// using System.Threading.Tasks; - -// namespace Microsoft.OneFuzz.Service; - - -// public interface IWebhookMessageLogOperations : IOrm -// { -// IAsyncEnumerable SearchExpired(); -// public Async.Task ProcessFromQueue(WebhookMessageQueueObj obj); -// } - - -// public class WebhookMessageLogOperations : Orm, IWebhookMessageLogOperations -// { -// const int EXPIRE_DAYS = 7; -// const int MAX_TRIES = 5; - -// private readonly IQueue _queue; -// private readonly ILogTracer _log; -// private readonly IWebhookOperations _webhook; - -// public WebhookMessageLogOperations(IStorage storage, IQueue queue, ILogTracer log, IWebhookOperations webhook, IServiceConfig config) : base(storage, log, config) -// { -// _queue = queue; -// _log = log; -// _webhook = webhook; -// } - - -// public async Async.Task QueueWebhook(WebhookMessageLog webhookLog) -// { -// var obj = new WebhookMessageQueueObj(webhookLog.WebhookId, webhookLog.EventId); - -// TimeSpan? visibilityTimeout = webhookLog.State switch -// { -// WebhookMessageState.Queued => TimeSpan.Zero, -// WebhookMessageState.Retrying => TimeSpan.FromSeconds(30), -// _ => null -// }; - -// if (visibilityTimeout == null) -// { -// _log.WithTags( -// new[] { -// ("WebhookId", webhookLog.WebhookId.ToString()), -// ("EventId", webhookLog.EventId.ToString()) } -// ). -// Error($"invalid WebhookMessage queue state, not queuing. {webhookLog.WebhookId}:{webhookLog.EventId} - {webhookLog.State}"); -// } -// else -// { -// await _queue.QueueObject("webhooks", obj, StorageType.Config, visibilityTimeout: visibilityTimeout); -// } -// } - -// public async Async.Task ProcessFromQueue(WebhookMessageQueueObj obj) -// { -// var message = await GetWebhookMessageById(obj.WebhookId, obj.EventId); - -// if (message == null) -// { -// _log.WithTags( -// new[] { -// ("WebhookId", obj.WebhookId.ToString()), -// ("EventId", obj.EventId.ToString()) } -// ). -// Error($"webhook message log not found for webhookId: {obj.WebhookId} and eventId: {obj.EventId}"); -// } -// else -// { -// await Process(message); -// } -// } - -// private async System.Threading.Tasks.Task Process(WebhookMessageLog message) -// { - -// if (message.State == WebhookMessageState.Failed || message.State == WebhookMessageState.Succeeded) -// { -// _log.WithTags( -// new[] { -// ("WebhookId", message.WebhookId.ToString()), -// ("EventId", message.EventId.ToString()) } -// ). -// Error($"webhook message already handled. {message.WebhookId}:{message.EventId}"); -// return; -// } - -// var newMessage = message with { TryCount = message.TryCount + 1 }; - -// _log.Info($"sending webhook: {message.WebhookId}:{message.EventId}"); -// var success = await Send(newMessage); -// if (success) -// { -// newMessage = newMessage with { State = WebhookMessageState.Succeeded }; -// await Replace(newMessage); -// _log.Info($"sent webhook event {newMessage.WebhookId}:{newMessage.EventId}"); -// } -// else if (newMessage.TryCount < MAX_TRIES) -// { -// newMessage = newMessage with { State = WebhookMessageState.Retrying }; -// await Replace(newMessage); -// await QueueWebhook(newMessage); -// _log.Warning($"sending webhook event failed, re-queued {newMessage.WebhookId}:{newMessage.EventId}"); -// } -// else -// { -// newMessage = newMessage with { State = WebhookMessageState.Failed }; -// await Replace(newMessage); -// _log.Info($"sending webhook: {newMessage.WebhookId} event: {newMessage.EventId} failed {newMessage.TryCount} times."); -// } - -// } - -// private async Task Send(WebhookMessageLog message) -// { -// var webhook = await _webhook.GetByWebhookId(message.WebhookId); -// if (webhook == null) -// { -// _log.WithTags( -// new[] { -// ("WebhookId", message.WebhookId.ToString()), -// } -// ). -// Error($"webhook not found for webhookId: {message.WebhookId}"); -// return false; -// } - -// try -// { -// return await _webhook.Send(message); -// } -// catch (Exception) -// { -// _log.WithTags( -// new[] { -// ("WebhookId", message.WebhookId.ToString()) -// } -// ). -// Error($"webhook send failed. {message.WebhookId}"); -// return false; -// } - -// } - -// private void QueueObject(string v, WebhookMessageQueueObj obj, StorageType config, int? visibility_timeout) -// { -// throw new NotImplementedException(); -// } - -// public IAsyncEnumerable SearchExpired() -// { -// var expireTime = (DateTimeOffset.UtcNow - TimeSpan.FromDays(EXPIRE_DAYS)).ToString("o"); - -// var timeFilter = $"Timestamp lt datetime'{expireTime}'"; -// return QueryAsync(filter: timeFilter); -// } - -// public async Async.Task GetWebhookMessageById(Guid webhookId, Guid eventId) -// { -// var data = QueryAsync(filter: $"PartitionKey eq '{webhookId}' and Rowkey eq '{eventId}'"); - -// return await data.FirstOrDefaultAsync(); -// } -// } diff --git a/src/ApiService/Tests/OrmTest.cs b/src/ApiService/Tests/OrmTest.cs index bd042e608a..cd49d44b53 100644 --- a/src/ApiService/Tests/OrmTest.cs +++ b/src/ApiService/Tests/OrmTest.cs @@ -246,19 +246,6 @@ public void TestIntKey() { Assert.Equal(expected.TheName, actual.TheName); } - - [Fact] - public void TestEventSerialization2() { - - var converter = new EntityConverter(); - var expectedEvent = new EventMessage(Guid.NewGuid(), EventType.NodeHeartbeat, new EventNodeHeartbeat(Guid.NewGuid(), Guid.NewGuid(), "test Poool"), Guid.NewGuid(), "test") { - ETag = new Azure.ETag("33a64df551425fcc55e4d42a148795d9f25f89d4") - }; - var te = converter.ToTableEntity(expectedEvent); - var actualEvent = converter.ToRecord(te); - Assert.Equal(expectedEvent, actualEvent); - } - record Entity3( [PartitionKey] int Id, [RowKey] string TheName, From ad9e58854548ba052550862aa20d2a4f54e267b2 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 26 Apr 2022 11:54:16 -0700 Subject: [PATCH 24/25] Using config. --- src/ApiService/ApiService/ServiceConfiguration.cs | 4 ++-- src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ApiService/ApiService/ServiceConfiguration.cs b/src/ApiService/ApiService/ServiceConfiguration.cs index 416c324f63..cf4cc75899 100644 --- a/src/ApiService/ApiService/ServiceConfiguration.cs +++ b/src/ApiService/ApiService/ServiceConfiguration.cs @@ -35,7 +35,7 @@ public interface IServiceConfig { public string? OneFuzzResourceGroup { get; } public string? OneFuzzTelemetry { get; } - public string OnefuzzVersion { get; } + public string OneFuzzVersion { get; } } public class ServiceConfiguration : IServiceConfig { @@ -77,5 +77,5 @@ public ServiceConfiguration() { public string? OneFuzzOwner { get => Environment.GetEnvironmentVariable("ONEFUZZ_OWNER"); } public string? OneFuzzResourceGroup { get => Environment.GetEnvironmentVariable("ONEFUZZ_RESOURCE_GROUP"); } public string? OneFuzzTelemetry { get => Environment.GetEnvironmentVariable("ONEFUZZ_TELEMETRY"); } - public string OnefuzzVersion { get => Environment.GetEnvironmentVariable("ONEFUZZ_VERSION") ?? "0.0.0"; } + public string OneFuzzVersion { get => Environment.GetEnvironmentVariable("ONEFUZZ_VERSION") ?? "0.0.0"; } } diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index 9376ba0a36..b58c8a283f 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -15,8 +15,6 @@ public interface IWebhookOperations { public class WebhookOperations : Orm, IWebhookOperations { - // Needs to eventually pull from global __version__ - const string USER_AGENT = "onefuzz-webhook 0.0.0"; private readonly IWebhookMessageLogOperations _webhookMessageLogOperations; private readonly ILogTracer _log; private readonly ICreds _creds; @@ -67,7 +65,7 @@ public async Async.Task Send(WebhookMessageLog messageLog) { var (data, digest) = await BuildMessage(webhookId: webhook.WebhookId, eventId: messageLog.EventId, eventType: messageLog.EventType, webhookEvent: messageLog.Event, secretToken: webhook.SecretToken, messageFormat: webhook.MessageFormat); - var headers = new Dictionary { { "User-Agent", USER_AGENT } }; + var headers = new Dictionary { { "User-Agent", $"onefuzz-webhook {_config.OneFuzzVersion}" } }; if (digest != null) { headers["X-Onefuzz-Digest"] = digest; From be3bb3ced34b958237043371375d1bd408cf1078 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 26 Apr 2022 12:29:42 -0700 Subject: [PATCH 25/25] Fixing ProxyOperations. --- src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs index 847bfa1367..6c4697039a 100644 --- a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs @@ -55,7 +55,7 @@ public ProxyOperations(ILogTracer log, IStorage storage, IEvents events, IProxyF } _logTracer.Info($"creating proxy: region:{region}"); - var newProxy = new Proxy(region, Guid.NewGuid(), DateTimeOffset.UtcNow, VmState.Init, Auth.BuildAuth(), null, null, _config.OnefuzzVersion, null, false); + var newProxy = new Proxy(region, Guid.NewGuid(), DateTimeOffset.UtcNow, VmState.Init, Auth.BuildAuth(), null, null, _config.OneFuzzVersion, null, false); await Replace(newProxy); await _events.SendEvent(new EventProxyCreated(region, newProxy.ProxyId)); @@ -83,8 +83,8 @@ public bool IsOutdated(Proxy proxy) { return false; } - if (proxy.Version != _config.OnefuzzVersion) { - _logTracer.Info($"mismatch version: proxy:{proxy.Version} service:{_config.OnefuzzVersion} state:{proxy.State}"); + if (proxy.Version != _config.OneFuzzVersion) { + _logTracer.Info($"mismatch version: proxy:{proxy.Version} service:{_config.OneFuzzVersion} state:{proxy.State}"); return true; }