From b918a82255f855dd484dfdd940b554f830360acb Mon Sep 17 00:00:00 2001 From: Volodymyr Smirnov Date: Sun, 1 Nov 2020 22:25:48 +0200 Subject: [PATCH] basic architecture change: consul + hangfire --- .../.idea/runConfigurations/Dummy_API.xml | 2 +- .../Attributes/MaxFileSizeAttribute.cs | 5 +- .../Controllers/QueueController.cs | 2 +- .../Controllers/ScanResultsController.cs | 2 +- .../Data/Configuration/ScanBackend.cs | 18 --- .../Data/Models/ScanResultEntry.cs | 28 ----- .../Data/{Models => }/ScanResult.cs | 9 +- .../Extensions/ServiceCollectionExtensions.cs | 4 +- .../MalwareMultiScan.Api.csproj | 2 + MalwareMultiScan.Api/Program.cs | 21 ++-- .../Implementations/ReceiverHostedService.cs | 103 ------------------ .../Implementations/ScanBackendService.cs | 64 ----------- .../Interfaces/IReceiverHostedService.cs | 11 -- .../Interfaces/IScanBackendService.cs | 25 ----- .../Services/Interfaces/IScanResultService.cs | 11 +- .../Services/ScanResultJob.cs | 82 ++++++++++++++ .../ScanResultService.cs | 67 ++++++++---- MalwareMultiScan.Api/Startup.cs | 34 +++--- MalwareMultiScan.Api/appsettings.json | 17 ++- .../Backends/Abstracts/AbstractScanBackend.cs | 2 +- .../ClamavScanBackend.cs | 2 +- .../ComodoScanBackend.cs | 2 +- .../{Implementations => }/DrWebScanBackend.cs | 2 +- .../{Implementations => }/DummyScanBackend.cs | 4 +- .../{ => Backends}/Interfaces/IScanBackend.cs | 2 +- .../{Implementations => }/KesScanBackend.cs | 2 +- .../McAfeeScanBackend.cs | 2 +- .../SophosScanBackend.cs | 2 +- .../WindowsDefenderScanBackend.cs | 2 +- .../Enums/BackendType.cs | 48 -------- .../Extensions/ServiceCollectionExtensions.cs | 45 +++----- .../MalwareMultiScan.Backends.csproj | 6 +- .../Messages/ScanRequestMessage.cs | 20 ---- .../Messages/ScanResultMessage.cs | 33 ------ .../{Implementations => }/ProcessRunner.cs | 0 MalwareMultiScan.Scanner/Dockerfile | 16 --- .../MalwareMultiScan.Scanner.csproj | 21 ---- MalwareMultiScan.Scanner/Program.cs | 45 -------- .../Implementations/ScanHostedService.cs | 57 ---------- .../Services/Interfaces/IScanBackgroundJob.cs | 17 --- .../Services/Interfaces/IScanHostedService.cs | 11 -- MalwareMultiScan.Scanner/appsettings.json | 19 ---- .../MalwareMultiScan.ScannerWorker.csproj | 15 +++ MalwareMultiScan.ScannerWorker/Program.cs | 50 +++++++++ .../Properties/launchSettings.json | 8 ++ .../Services/ConsulHostedService.cs | 82 ++++++++++++++ .../Services}/ScanBackgroundJob.cs | 63 ++++++----- .../appsettings.json | 9 ++ .../Enums/ScanResultStatus.cs | 9 ++ .../Extensions/ServiceCollectionExtensions.cs | 40 +++++++ .../MalwareMultiScan.Shared.csproj | 20 ++++ .../Message/ScanQueueMessage.cs | 10 ++ .../Message/ScanResultMessage.cs | 13 +++ .../Services/Interfaces/IScanBackgroundJob.cs | 12 ++ .../Services/Interfaces/IScanResultJob.cs | 16 +++ .../Api/ControllersTests.cs | 2 +- .../Api/ReceiverHostedServiceTests.cs | 3 - .../Api/ScanBackendServiceTests.cs | 6 +- .../Api/ScanResultServiceTests.cs | 4 +- .../Backends/BackendsTests.cs | 3 +- .../Scanner/ScanBackgroundJobTests.cs | 6 - .../Scanner/ScanHostedServiceTests.cs | 5 - .../models/scan-result-entry-flattened.ts | 22 +++- .../models/scan-result-entry.ts | 5 +- MalwareMultiScan.Ui/pages/_id/index.vue | 11 +- MalwareMultiScan.sln | 18 ++- 66 files changed, 571 insertions(+), 728 deletions(-) delete mode 100644 MalwareMultiScan.Api/Data/Configuration/ScanBackend.cs delete mode 100644 MalwareMultiScan.Api/Data/Models/ScanResultEntry.cs rename MalwareMultiScan.Api/Data/{Models => }/ScanResult.cs (71%) delete mode 100644 MalwareMultiScan.Api/Services/Implementations/ReceiverHostedService.cs delete mode 100644 MalwareMultiScan.Api/Services/Implementations/ScanBackendService.cs delete mode 100644 MalwareMultiScan.Api/Services/Interfaces/IReceiverHostedService.cs delete mode 100644 MalwareMultiScan.Api/Services/Interfaces/IScanBackendService.cs create mode 100644 MalwareMultiScan.Api/Services/ScanResultJob.cs rename MalwareMultiScan.Api/Services/{Implementations => }/ScanResultService.cs (59%) rename MalwareMultiScan.Backends/Backends/{Implementations => }/ClamavScanBackend.cs (94%) rename MalwareMultiScan.Backends/Backends/{Implementations => }/ComodoScanBackend.cs (93%) rename MalwareMultiScan.Backends/Backends/{Implementations => }/DrWebScanBackend.cs (93%) rename MalwareMultiScan.Backends/Backends/{Implementations => }/DummyScanBackend.cs (89%) rename MalwareMultiScan.Backends/{ => Backends}/Interfaces/IScanBackend.cs (95%) rename MalwareMultiScan.Backends/Backends/{Implementations => }/KesScanBackend.cs (93%) rename MalwareMultiScan.Backends/Backends/{Implementations => }/McAfeeScanBackend.cs (94%) rename MalwareMultiScan.Backends/Backends/{Implementations => }/SophosScanBackend.cs (94%) rename MalwareMultiScan.Backends/Backends/{Implementations => }/WindowsDefenderScanBackend.cs (94%) delete mode 100644 MalwareMultiScan.Backends/Enums/BackendType.cs delete mode 100644 MalwareMultiScan.Backends/Messages/ScanRequestMessage.cs delete mode 100644 MalwareMultiScan.Backends/Messages/ScanResultMessage.cs rename MalwareMultiScan.Backends/Services/{Implementations => }/ProcessRunner.cs (100%) delete mode 100644 MalwareMultiScan.Scanner/Dockerfile delete mode 100644 MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj delete mode 100644 MalwareMultiScan.Scanner/Program.cs delete mode 100644 MalwareMultiScan.Scanner/Services/Implementations/ScanHostedService.cs delete mode 100644 MalwareMultiScan.Scanner/Services/Interfaces/IScanBackgroundJob.cs delete mode 100644 MalwareMultiScan.Scanner/Services/Interfaces/IScanHostedService.cs delete mode 100644 MalwareMultiScan.Scanner/appsettings.json create mode 100644 MalwareMultiScan.ScannerWorker/MalwareMultiScan.ScannerWorker.csproj create mode 100644 MalwareMultiScan.ScannerWorker/Program.cs create mode 100644 MalwareMultiScan.ScannerWorker/Properties/launchSettings.json create mode 100644 MalwareMultiScan.ScannerWorker/Services/ConsulHostedService.cs rename {MalwareMultiScan.Scanner/Services/Implementations => MalwareMultiScan.ScannerWorker/Services}/ScanBackgroundJob.cs (55%) create mode 100644 MalwareMultiScan.ScannerWorker/appsettings.json create mode 100644 MalwareMultiScan.Shared/Enums/ScanResultStatus.cs create mode 100644 MalwareMultiScan.Shared/Extensions/ServiceCollectionExtensions.cs create mode 100644 MalwareMultiScan.Shared/MalwareMultiScan.Shared.csproj create mode 100644 MalwareMultiScan.Shared/Message/ScanQueueMessage.cs create mode 100644 MalwareMultiScan.Shared/Message/ScanResultMessage.cs create mode 100644 MalwareMultiScan.Shared/Services/Interfaces/IScanBackgroundJob.cs create mode 100644 MalwareMultiScan.Shared/Services/Interfaces/IScanResultJob.cs diff --git a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dummy_API.xml b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dummy_API.xml index 0c4f862..0275fa7 100644 --- a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dummy_API.xml +++ b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dummy_API.xml @@ -1,6 +1,6 @@ - + diff --git a/MalwareMultiScan.Api/Attributes/MaxFileSizeAttribute.cs b/MalwareMultiScan.Api/Attributes/MaxFileSizeAttribute.cs index 5aaa873..02200a3 100644 --- a/MalwareMultiScan.Api/Attributes/MaxFileSizeAttribute.cs +++ b/MalwareMultiScan.Api/Attributes/MaxFileSizeAttribute.cs @@ -15,7 +15,10 @@ namespace MalwareMultiScan.Api.Attributes { var maxSize = validationContext .GetRequiredService() - .GetValue("MaxFileSize"); + .GetValue("FILE_SIZE_LIMIT"); + + if (maxSize == 0) + return ValidationResult.Success; var formFile = (IFormFile) value; diff --git a/MalwareMultiScan.Api/Controllers/QueueController.cs b/MalwareMultiScan.Api/Controllers/QueueController.cs index bbee862..565f8f3 100644 --- a/MalwareMultiScan.Api/Controllers/QueueController.cs +++ b/MalwareMultiScan.Api/Controllers/QueueController.cs @@ -2,7 +2,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using MalwareMultiScan.Api.Attributes; -using MalwareMultiScan.Api.Data.Models; +using MalwareMultiScan.Api.Data; using MalwareMultiScan.Api.Services.Interfaces; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/MalwareMultiScan.Api/Controllers/ScanResultsController.cs b/MalwareMultiScan.Api/Controllers/ScanResultsController.cs index c75dc4f..d10b146 100644 --- a/MalwareMultiScan.Api/Controllers/ScanResultsController.cs +++ b/MalwareMultiScan.Api/Controllers/ScanResultsController.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using MalwareMultiScan.Api.Data.Models; +using MalwareMultiScan.Api.Data; using MalwareMultiScan.Api.Services.Interfaces; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/MalwareMultiScan.Api/Data/Configuration/ScanBackend.cs b/MalwareMultiScan.Api/Data/Configuration/ScanBackend.cs deleted file mode 100644 index f75111c..0000000 --- a/MalwareMultiScan.Api/Data/Configuration/ScanBackend.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace MalwareMultiScan.Api.Data.Configuration -{ - /// - /// Scan backend. - /// - public class ScanBackend - { - /// - /// Backend id. - /// - public string Id { get; set; } - - /// - /// Backend state. - /// - public bool Enabled { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Data/Models/ScanResultEntry.cs b/MalwareMultiScan.Api/Data/Models/ScanResultEntry.cs deleted file mode 100644 index eca493d..0000000 --- a/MalwareMultiScan.Api/Data/Models/ScanResultEntry.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace MalwareMultiScan.Api.Data.Models -{ - /// - /// Scan result entry. - /// - public class ScanResultEntry - { - /// - /// Completion status. - /// - public bool Completed { get; set; } - - /// - /// Indicates that scanning completed without error. - /// - public bool? Succeeded { get; set; } - - /// - /// Scanning duration in seconds. - /// - public long Duration { get; set; } - - /// - /// Detected names of threats. - /// - public string[] Threats { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Data/Models/ScanResult.cs b/MalwareMultiScan.Api/Data/ScanResult.cs similarity index 71% rename from MalwareMultiScan.Api/Data/Models/ScanResult.cs rename to MalwareMultiScan.Api/Data/ScanResult.cs index 4a9ddbe..ec97462 100644 --- a/MalwareMultiScan.Api/Data/Models/ScanResult.cs +++ b/MalwareMultiScan.Api/Data/ScanResult.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; +using MalwareMultiScan.Shared.Message; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; -namespace MalwareMultiScan.Api.Data.Models +namespace MalwareMultiScan.Api.Data { /// /// Scan result. @@ -23,9 +24,9 @@ namespace MalwareMultiScan.Api.Data.Models public Uri CallbackUrl { get; set; } /// - /// Result entries where key is backend id and value is . + /// Result entries where key is backend id and value is . /// - public Dictionary Results { get; set; } = - new Dictionary(); + public Dictionary Results { get; set; } = + new Dictionary(); } } \ No newline at end of file diff --git a/MalwareMultiScan.Api/Extensions/ServiceCollectionExtensions.cs b/MalwareMultiScan.Api/Extensions/ServiceCollectionExtensions.cs index 5c18425..06a9f4a 100644 --- a/MalwareMultiScan.Api/Extensions/ServiceCollectionExtensions.cs +++ b/MalwareMultiScan.Api/Extensions/ServiceCollectionExtensions.cs @@ -14,8 +14,8 @@ namespace MalwareMultiScan.Api.Extensions { internal static void AddMongoDb(this IServiceCollection services, IConfiguration configuration) { - var client = new MongoClient(configuration.GetConnectionString("Mongo")); - var db = client.GetDatabase(configuration.GetValue("DatabaseName")); + var client = new MongoClient(configuration.GetValue("MONGO_ADDRESS")); + var db = client.GetDatabase(configuration.GetValue("MONGO_DATABASE")); services.AddSingleton(client); services.AddSingleton(db); diff --git a/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj b/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj index ff27e1b..b4cd3be 100644 --- a/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj +++ b/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj @@ -16,6 +16,8 @@ + + diff --git a/MalwareMultiScan.Api/Program.cs b/MalwareMultiScan.Api/Program.cs index d8993a8..bc8874c 100644 --- a/MalwareMultiScan.Api/Program.cs +++ b/MalwareMultiScan.Api/Program.cs @@ -1,21 +1,24 @@ -using EasyNetQ.LightInject; +using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; namespace MalwareMultiScan.Api { - [ExcludeFromCodeCoverage] internal static class Program { - public static void Main(string[] args) + public static async Task Main(string[] args) { - CreateHostBuilder(args).Build().Run(); - } + await Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(builder => + { + builder.ConfigureKestrel( + options => options.Limits.MaxRequestBodySize = long.MaxValue); - private static IHostBuilder CreateHostBuilder(string[] args) - { - return Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()); + builder.UseStartup(); + }) + .UseConsoleLifetime() + .Build() + .RunAsync(); } } } \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Implementations/ReceiverHostedService.cs b/MalwareMultiScan.Api/Services/Implementations/ReceiverHostedService.cs deleted file mode 100644 index 02b3452..0000000 --- a/MalwareMultiScan.Api/Services/Implementations/ReceiverHostedService.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Api.Services.Interfaces; -using MalwareMultiScan.Backends.Messages; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; - -namespace MalwareMultiScan.Api.Services.Implementations -{ - /// - public class ReceiverHostedService : IReceiverHostedService - { - private readonly IBus _bus; - private readonly IConfiguration _configuration; - private readonly IHttpClientFactory _httpClientFactory; - private readonly ILogger _logger; - private readonly IScanResultService _scanResultService; - - /// - /// Initialize receiver hosted service. - /// - /// EasyNetQ bus. - /// Configuration. - /// Scan result service. - /// Logger. - /// HTTP client factory. - public ReceiverHostedService(IBus bus, IConfiguration configuration, IScanResultService scanResultService, - ILogger logger, IHttpClientFactory httpClientFactory) - { - _bus = bus; - _configuration = configuration; - _scanResultService = scanResultService; - _logger = logger; - _httpClientFactory = httpClientFactory; - } - - - /// - public Task StartAsync(CancellationToken cancellationToken) - { - _bus.Receive( - _configuration.GetValue("ResultsSubscriptionId"), StoreScanResult); - - _logger.LogInformation( - "Started hosted service for receiving scan results"); - - return Task.CompletedTask; - } - - /// - public Task StopAsync(CancellationToken cancellationToken) - { - _bus?.Dispose(); - - _logger.LogInformation( - "Stopped hosted service for receiving scan results"); - - return Task.CompletedTask; - } - - private async Task StoreScanResult(ScanResultMessage message) - { - message.Threats ??= new string[] { }; - - _logger.LogInformation( - $"Received a result from {message.Backend} for {message.Id} " + - $"with threats {string.Join(",", message.Threats)}"); - - await _scanResultService.UpdateScanResultForBackend( - message.Id, message.Backend, message.Duration, true, - message.Succeeded, message.Threats); - - var result = await _scanResultService.GetScanResult(message.Id); - - if (result?.CallbackUrl == null) - return; - - var cancellationTokenSource = new CancellationTokenSource( - TimeSpan.FromSeconds(3)); - - using var httpClient = _httpClientFactory.CreateClient(); - - try - { - var response = await httpClient.PostAsync( - result.CallbackUrl, - new StringContent(JsonConvert.SerializeObject(result), Encoding.UTF8, "application/json"), - cancellationTokenSource.Token); - - response.EnsureSuccessStatusCode(); - } - catch (Exception exception) - { - _logger.LogError(exception, $"Failed to POST to callback URL {result.CallbackUrl}"); - } - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Implementations/ScanBackendService.cs b/MalwareMultiScan.Api/Services/Implementations/ScanBackendService.cs deleted file mode 100644 index 1e5cc33..0000000 --- a/MalwareMultiScan.Api/Services/Implementations/ScanBackendService.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Api.Data.Configuration; -using MalwareMultiScan.Api.Data.Models; -using MalwareMultiScan.Api.Services.Interfaces; -using MalwareMultiScan.Backends.Messages; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - -namespace MalwareMultiScan.Api.Services.Implementations -{ - /// - public class ScanBackendService : IScanBackendService - { - private readonly IBus _bus; - private readonly ILogger _logger; - - /// - /// Initialise scan backend service. - /// - /// Configuration. - /// EasyNetQ bus. - /// Logger. - /// Missing backends.yaml configuration. - public ScanBackendService(IConfiguration configuration, IBus bus, ILogger logger) - { - _bus = bus; - _logger = logger; - - var configurationPath = configuration.GetValue("BackendsConfiguration"); - - if (!File.Exists(configurationPath)) - throw new FileNotFoundException("Missing BackendsConfiguration YAML file", configurationPath); - - var configurationContent = File.ReadAllText(configurationPath); - - var deserializer = new DeserializerBuilder() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .Build(); - - List = deserializer.Deserialize(configurationContent); - } - - /// - public ScanBackend[] List { get; } - - /// - public async Task QueueUrlScan(ScanResult result, ScanBackend backend, string fileUrl) - { - _logger.LogInformation( - $"Queueing scan for {result.Id} on {backend.Id} at {fileUrl}"); - - await _bus.SendAsync(backend.Id, new ScanRequestMessage - { - Id = result.Id, - Uri = new Uri(fileUrl) - }); - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Interfaces/IReceiverHostedService.cs b/MalwareMultiScan.Api/Services/Interfaces/IReceiverHostedService.cs deleted file mode 100644 index 67ed8f0..0000000 --- a/MalwareMultiScan.Api/Services/Interfaces/IReceiverHostedService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Extensions.Hosting; - -namespace MalwareMultiScan.Api.Services.Interfaces -{ - /// - /// Receiver hosted service. - /// - public interface IReceiverHostedService : IHostedService - { - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Interfaces/IScanBackendService.cs b/MalwareMultiScan.Api/Services/Interfaces/IScanBackendService.cs deleted file mode 100644 index 3cc6484..0000000 --- a/MalwareMultiScan.Api/Services/Interfaces/IScanBackendService.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading.Tasks; -using MalwareMultiScan.Api.Data.Configuration; -using MalwareMultiScan.Api.Data.Models; - -namespace MalwareMultiScan.Api.Services.Interfaces -{ - /// - /// Scan backend service. - /// - public interface IScanBackendService - { - /// - /// Get list of parsed backends. - /// - ScanBackend[] List { get; } - - /// - /// Queue URL for scan. - /// - /// Result entry. - /// Backend entry. - /// Remote URL. - Task QueueUrlScan(ScanResult result, ScanBackend backend, string fileUrl); - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Interfaces/IScanResultService.cs b/MalwareMultiScan.Api/Services/Interfaces/IScanResultService.cs index 9f0b6d9..3058316 100644 --- a/MalwareMultiScan.Api/Services/Interfaces/IScanResultService.cs +++ b/MalwareMultiScan.Api/Services/Interfaces/IScanResultService.cs @@ -1,7 +1,8 @@ using System; using System.IO; using System.Threading.Tasks; -using MalwareMultiScan.Api.Data.Models; +using MalwareMultiScan.Api.Data; +using MalwareMultiScan.Shared.Message; namespace MalwareMultiScan.Api.Services.Interfaces { @@ -29,12 +30,8 @@ namespace MalwareMultiScan.Api.Services.Interfaces /// /// Result id. /// Backend id. - /// Duration. - /// Completion status. - /// Indicates that scanning completed without error. - /// Detected names of threats. - Task UpdateScanResultForBackend(string resultId, string backendId, long duration, - bool completed = false, bool succeeded = false, string[] threats = null); + /// Scan result. + Task UpdateScanResultForBackend(string resultId, string backendId, ScanResultMessage result = null); /// /// Queue URL for scanning. diff --git a/MalwareMultiScan.Api/Services/ScanResultJob.cs b/MalwareMultiScan.Api/Services/ScanResultJob.cs new file mode 100644 index 0000000..540bd7b --- /dev/null +++ b/MalwareMultiScan.Api/Services/ScanResultJob.cs @@ -0,0 +1,82 @@ +using System; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Hangfire; +using Hangfire.States; +using MalwareMultiScan.Api.Data; +using MalwareMultiScan.Api.Services.Interfaces; +using MalwareMultiScan.Shared.Message; +using MalwareMultiScan.Shared.Services.Interfaces; +using Microsoft.Extensions.Logging; + +namespace MalwareMultiScan.Api.Services +{ + public class ScanResultJob : IScanResultJob + { + private readonly IBackgroundJobClient _backgroundJobClient; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger _logger; + private readonly IScanResultService _scanResultService; + + public ScanResultJob( + ILogger logger, + IHttpClientFactory httpClientFactory, + IScanResultService scanResultService, + IBackgroundJobClient backgroundJobClient) + { + _logger = logger; + _httpClientFactory = httpClientFactory; + _scanResultService = scanResultService; + _backgroundJobClient = backgroundJobClient; + } + + /// + public async Task Report(string resultId, string backendId, ScanResultMessage result) + { + _logger.LogInformation( + $"Received a result from {backendId} for {result} with status {result.Status} " + + $"and threats {string.Join(",", result.Threats)}"); + + await _scanResultService.UpdateScanResultForBackend(resultId, backendId, result); + + var scanResult = await _scanResultService.GetScanResult(resultId); + + if (scanResult?.CallbackUrl == null) + return; + + _backgroundJobClient.Create( + x => x.Notify(scanResult.CallbackUrl, resultId, backendId, result), new EnqueuedState()); + } + + /// + public async Task Notify(Uri uri, string resultId, string backendId, ScanResultMessage result) + { + var cancellationTokenSource = new CancellationTokenSource( + TimeSpan.FromSeconds(3)); + + using var httpClient = _httpClientFactory.CreateClient(); + + try + { + var builder = new UriBuilder(uri) + { + Query = $"?id={resultId}" + + $"&backend={backendId}" + }; + + var response = await httpClient.PostAsync(builder.Uri, + new StringContent(JsonSerializer.Serialize(result), Encoding.UTF8, "application/json"), + cancellationTokenSource.Token); + + response.EnsureSuccessStatusCode(); + } + catch (Exception exception) + { + _logger.LogError(exception, $"Failed to POST to callback URL {uri}"); + } + } + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Implementations/ScanResultService.cs b/MalwareMultiScan.Api/Services/ScanResultService.cs similarity index 59% rename from MalwareMultiScan.Api/Services/Implementations/ScanResultService.cs rename to MalwareMultiScan.Api/Services/ScanResultService.cs index 51b1812..7a906c2 100644 --- a/MalwareMultiScan.Api/Services/Implementations/ScanResultService.cs +++ b/MalwareMultiScan.Api/Services/ScanResultService.cs @@ -2,33 +2,45 @@ using System; using System.IO; using System.Linq; using System.Threading.Tasks; -using MalwareMultiScan.Api.Data.Models; +using Consul; +using Hangfire; +using Hangfire.States; +using MalwareMultiScan.Api.Data; using MalwareMultiScan.Api.Services.Interfaces; +using MalwareMultiScan.Shared.Message; +using MalwareMultiScan.Shared.Services.Interfaces; using MongoDB.Bson; using MongoDB.Driver; using MongoDB.Driver.GridFS; -namespace MalwareMultiScan.Api.Services.Implementations +namespace MalwareMultiScan.Api.Services { /// public class ScanResultService : IScanResultService { private const string CollectionName = "ScanResults"; + private readonly IBackgroundJobClient _backgroundJobClient; private readonly IGridFSBucket _bucket; private readonly IMongoCollection _collection; - private readonly IScanBackendService _scanBackendService; + private readonly IConsulClient _consulClient; /// /// Initialize scan result service. /// /// Mongo database. /// GridFS bucket. - /// Scan backend service. - public ScanResultService(IMongoDatabase db, IGridFSBucket bucket, IScanBackendService scanBackendService) + /// Consul client. + /// Background job client. + public ScanResultService( + IMongoDatabase db, + IGridFSBucket bucket, + IConsulClient consulClient, + IBackgroundJobClient backgroundJobClient) { _bucket = bucket; - _scanBackendService = scanBackendService; + _consulClient = consulClient; + _backgroundJobClient = backgroundJobClient; _collection = db.GetCollection(CollectionName); } @@ -38,11 +50,7 @@ namespace MalwareMultiScan.Api.Services.Implementations { var scanResult = new ScanResult { - CallbackUrl = callbackUrl, - - Results = _scanBackendService.List - .Where(b => b.Enabled) - .ToDictionary(k => k.Id, v => new ScanResultEntry()) + CallbackUrl = callbackUrl }; await _collection.InsertOneAsync(scanResult); @@ -60,25 +68,40 @@ namespace MalwareMultiScan.Api.Services.Implementations } /// - public async Task UpdateScanResultForBackend(string resultId, string backendId, long duration, - bool completed = false, bool succeeded = false, string[] threats = null) + public async Task UpdateScanResultForBackend(string resultId, string backendId, + ScanResultMessage result = null) { + result ??= new ScanResultMessage(); + await _collection.UpdateOneAsync( Builders.Filter.Where(r => r.Id == resultId), - Builders.Update.Set(r => r.Results[backendId], new ScanResultEntry - { - Completed = completed, - Succeeded = succeeded, - Duration = duration, - Threats = threats ?? new string[] { } - })); + Builders.Update.Set(r => r.Results[backendId], result)); } /// public async Task QueueUrlScan(ScanResult result, string fileUrl) { - foreach (var backend in _scanBackendService.List.Where(b => b.Enabled)) - await _scanBackendService.QueueUrlScan(result, backend, fileUrl); + var message = new ScanQueueMessage + { + Id = result.Id, + Uri = new Uri(fileUrl) + }; + + var scanners = await _consulClient.Health.Service("scanner", null, true); + + var backends = scanners.Response + .Select(s => s.Service.Meta.TryGetValue("BackendId", out var backendId) ? backendId : null) + .Where(q => q != null) + .Distinct() + .ToArray(); + + foreach (var backend in backends) + { + await UpdateScanResultForBackend(result.Id, backend); + + _backgroundJobClient.Create( + j => j.Process(message), new EnqueuedState(backend)); + } } /// diff --git a/MalwareMultiScan.Api/Startup.cs b/MalwareMultiScan.Api/Startup.cs index 3e8624c..bcf01e9 100644 --- a/MalwareMultiScan.Api/Startup.cs +++ b/MalwareMultiScan.Api/Startup.cs @@ -1,16 +1,17 @@ using System.Diagnostics.CodeAnalysis; +using Hangfire; using MalwareMultiScan.Api.Extensions; -using MalwareMultiScan.Api.Services.Implementations; +using MalwareMultiScan.Api.Services; using MalwareMultiScan.Api.Services.Interfaces; -using MalwareMultiScan.Backends.Extensions; +using MalwareMultiScan.Shared.Extensions; +using MalwareMultiScan.Shared.Services.Interfaces; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace MalwareMultiScan.Api { - [ExcludeFromCodeCoverage] internal class Startup { private readonly IConfiguration _configuration; @@ -24,29 +25,26 @@ namespace MalwareMultiScan.Api { services.AddDockerForwardedHeadersOptions(); - services.Configure(options => - { - options.Limits.MaxRequestBodySize = _configuration.GetValue("MaxFileSize"); - }); - + services.AddConsul(_configuration); services.AddMongoDb(_configuration); - services.AddRabbitMq(_configuration); - - services.AddSingleton(); + services.AddHangfire(_configuration); + services.AddSingleton(); - + services.AddSingleton(); + services.AddControllers(); - - services.AddHostedService(); - services.AddHttpClient(); } - public void Configure(IApplicationBuilder app) + public void Configure(IApplicationBuilder app, IHostEnvironment hostEnvironment) { app.UseRouting(); - app.UseForwardedHeaders(); app.UseEndpoints(endpoints => endpoints.MapControllers()); + + app.UseForwardedHeaders(); + + if (hostEnvironment.IsDevelopment()) + app.UseHangfireDashboard(); } } } \ No newline at end of file diff --git a/MalwareMultiScan.Api/appsettings.json b/MalwareMultiScan.Api/appsettings.json index 0918519..23a48d7 100644 --- a/MalwareMultiScan.Api/appsettings.json +++ b/MalwareMultiScan.Api/appsettings.json @@ -7,15 +7,12 @@ } }, - "AllowedHosts": "*", - - "ConnectionStrings": { - "Mongo": "mongodb://localhost:27017", - "RabbitMQ": "host=localhost" - }, + "MONGO_ADDRESS": "mongodb://localhost:27017", + "MONGO_DATABASE": "MalwareMultiScan", - "DatabaseName": "MalwareMultiScan", - "ResultsSubscriptionId": "mms.results", - "MaxFileSize": 52428800, - "BackendsConfiguration": "backends.yaml" + "REDIS_ADDRESS": "localhost:6379", + + "CONSUL_ADDRESS": "http://localhost:8500", + + "FILE_SIZE_LIMIT": 52428800 } diff --git a/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs b/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs index 00a282a..7448219 100644 --- a/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs @@ -4,7 +4,7 @@ using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MalwareMultiScan.Backends.Interfaces; +using MalwareMultiScan.Backends.Backends.Interfaces; namespace MalwareMultiScan.Backends.Backends.Abstracts { diff --git a/MalwareMultiScan.Backends/Backends/Implementations/ClamavScanBackend.cs b/MalwareMultiScan.Backends/Backends/ClamavScanBackend.cs similarity index 94% rename from MalwareMultiScan.Backends/Backends/Implementations/ClamavScanBackend.cs rename to MalwareMultiScan.Backends/Backends/ClamavScanBackend.cs index d80fc16..18e461c 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/ClamavScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/ClamavScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class ClamavScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/ComodoScanBackend.cs b/MalwareMultiScan.Backends/Backends/ComodoScanBackend.cs similarity index 93% rename from MalwareMultiScan.Backends/Backends/Implementations/ComodoScanBackend.cs rename to MalwareMultiScan.Backends/Backends/ComodoScanBackend.cs index 6daee74..d93029c 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/ComodoScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/ComodoScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class ComodoScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/DrWebScanBackend.cs b/MalwareMultiScan.Backends/Backends/DrWebScanBackend.cs similarity index 93% rename from MalwareMultiScan.Backends/Backends/Implementations/DrWebScanBackend.cs rename to MalwareMultiScan.Backends/Backends/DrWebScanBackend.cs index 7681449..159918e 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/DrWebScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/DrWebScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class DrWebScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/DummyScanBackend.cs b/MalwareMultiScan.Backends/Backends/DummyScanBackend.cs similarity index 89% rename from MalwareMultiScan.Backends/Backends/Implementations/DummyScanBackend.cs rename to MalwareMultiScan.Backends/Backends/DummyScanBackend.cs index 6701648..13d1159 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/DummyScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/DummyScanBackend.cs @@ -2,9 +2,9 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using MalwareMultiScan.Backends.Interfaces; +using MalwareMultiScan.Backends.Backends.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class DummyScanBackend : IScanBackend diff --git a/MalwareMultiScan.Backends/Interfaces/IScanBackend.cs b/MalwareMultiScan.Backends/Backends/Interfaces/IScanBackend.cs similarity index 95% rename from MalwareMultiScan.Backends/Interfaces/IScanBackend.cs rename to MalwareMultiScan.Backends/Backends/Interfaces/IScanBackend.cs index b352ea0..ec2215d 100644 --- a/MalwareMultiScan.Backends/Interfaces/IScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/Interfaces/IScanBackend.cs @@ -3,7 +3,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -namespace MalwareMultiScan.Backends.Interfaces +namespace MalwareMultiScan.Backends.Backends.Interfaces { /// /// Scan backend. diff --git a/MalwareMultiScan.Backends/Backends/Implementations/KesScanBackend.cs b/MalwareMultiScan.Backends/Backends/KesScanBackend.cs similarity index 93% rename from MalwareMultiScan.Backends/Backends/Implementations/KesScanBackend.cs rename to MalwareMultiScan.Backends/Backends/KesScanBackend.cs index d1141ec..1481a34 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/KesScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/KesScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class KesScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/McAfeeScanBackend.cs b/MalwareMultiScan.Backends/Backends/McAfeeScanBackend.cs similarity index 94% rename from MalwareMultiScan.Backends/Backends/Implementations/McAfeeScanBackend.cs rename to MalwareMultiScan.Backends/Backends/McAfeeScanBackend.cs index f15a66f..6d97156 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/McAfeeScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/McAfeeScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class McAfeeScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/SophosScanBackend.cs b/MalwareMultiScan.Backends/Backends/SophosScanBackend.cs similarity index 94% rename from MalwareMultiScan.Backends/Backends/Implementations/SophosScanBackend.cs rename to MalwareMultiScan.Backends/Backends/SophosScanBackend.cs index 0479582..2f8f1d8 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/SophosScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/SophosScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class SophosScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/WindowsDefenderScanBackend.cs b/MalwareMultiScan.Backends/Backends/WindowsDefenderScanBackend.cs similarity index 94% rename from MalwareMultiScan.Backends/Backends/Implementations/WindowsDefenderScanBackend.cs rename to MalwareMultiScan.Backends/Backends/WindowsDefenderScanBackend.cs index 4db9613..1c57b29 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/WindowsDefenderScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/WindowsDefenderScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class WindowsDefenderScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Enums/BackendType.cs b/MalwareMultiScan.Backends/Enums/BackendType.cs deleted file mode 100644 index 5dda416..0000000 --- a/MalwareMultiScan.Backends/Enums/BackendType.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace MalwareMultiScan.Backends.Enums -{ - /// - /// Backend type. - /// - public enum BackendType - { - /// - /// Dummy - /// - Dummy, - - /// - /// Windows Defender. - /// - Defender, - - /// - /// ClamAV. - /// - Clamav, - - /// - /// DrWeb. - /// - DrWeb, - - /// - /// KES. - /// - Kes, - - /// - /// Comodo. - /// - Comodo, - - /// - /// Sophos. - /// - Sophos, - - /// - /// McAfee. - /// - McAfee - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs b/MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs index 0c2bf00..a5c7478 100644 --- a/MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs +++ b/MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs @@ -1,9 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; -using EasyNetQ; -using MalwareMultiScan.Backends.Backends.Implementations; -using MalwareMultiScan.Backends.Enums; -using MalwareMultiScan.Backends.Interfaces; +using MalwareMultiScan.Backends.Backends; +using MalwareMultiScan.Backends.Backends.Interfaces; using MalwareMultiScan.Backends.Services.Implementations; using MalwareMultiScan.Backends.Services.Interfaces; using Microsoft.Extensions.Configuration; @@ -17,55 +15,42 @@ namespace MalwareMultiScan.Backends.Extensions [ExcludeFromCodeCoverage] public static class ServiceCollectionExtensions { - /// - /// Add RabbitMQ service. - /// - /// Service collection. - /// Configuration. - public static void AddRabbitMq(this IServiceCollection services, IConfiguration configuration) - { - services.AddSingleton(x => - RabbitHutch.CreateBus(configuration.GetConnectionString("RabbitMQ"))); - } - /// /// Add scanning backend. /// /// Service collection. /// Configuration. /// Unknown backend. - public static void AddScanningBackend(this IServiceCollection services, IConfiguration configuration) + public static void AddScanBackend(this IServiceCollection services, IConfiguration configuration) { services.AddSingleton(); - switch (configuration.GetValue("BackendType")) + switch (configuration.GetValue("BACKEND_ID")) { - case BackendType.Dummy: - services.AddSingleton(); - break; - case BackendType.Defender: - services.AddSingleton(); - break; - case BackendType.Clamav: + case "clamav": services.AddSingleton(); break; - case BackendType.DrWeb: + case "drweb": services.AddSingleton(); break; - case BackendType.Kes: + case "kes": services.AddSingleton(); break; - case BackendType.Comodo: + case "comodo": services.AddSingleton(); break; - case BackendType.Sophos: + case "sophos": services.AddSingleton(); break; - case BackendType.McAfee: + case "mcafee": services.AddSingleton(); break; + case "windows-defender": + services.AddSingleton(); + break; default: - throw new ArgumentOutOfRangeException(); + services.AddSingleton(); + break; } } } diff --git a/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj b/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj index 7611bdd..e62ad89 100644 --- a/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj +++ b/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj @@ -10,11 +10,7 @@ - - - - - + diff --git a/MalwareMultiScan.Backends/Messages/ScanRequestMessage.cs b/MalwareMultiScan.Backends/Messages/ScanRequestMessage.cs deleted file mode 100644 index 011e49b..0000000 --- a/MalwareMultiScan.Backends/Messages/ScanRequestMessage.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace MalwareMultiScan.Backends.Messages -{ - /// - /// Scan request message. - /// - public class ScanRequestMessage - { - /// - /// Result id. - /// - public string Id { get; set; } - - /// - /// Remote URL. - /// - public Uri Uri { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Messages/ScanResultMessage.cs b/MalwareMultiScan.Backends/Messages/ScanResultMessage.cs deleted file mode 100644 index 01686ff..0000000 --- a/MalwareMultiScan.Backends/Messages/ScanResultMessage.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace MalwareMultiScan.Backends.Messages -{ - /// - /// Scan result message. - /// - public class ScanResultMessage - { - /// - /// Result id. - /// - public string Id { get; set; } - - /// - /// Backend. - /// - public string Backend { get; set; } - - /// - /// Status. - /// - public bool Succeeded { get; set; } - - /// - /// List of detected threats. - /// - public string[] Threats { get; set; } - - /// - /// Duration. - /// - public long Duration { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Services/Implementations/ProcessRunner.cs b/MalwareMultiScan.Backends/Services/ProcessRunner.cs similarity index 100% rename from MalwareMultiScan.Backends/Services/Implementations/ProcessRunner.cs rename to MalwareMultiScan.Backends/Services/ProcessRunner.cs diff --git a/MalwareMultiScan.Scanner/Dockerfile b/MalwareMultiScan.Scanner/Dockerfile deleted file mode 100644 index 56ccb3a..0000000 --- a/MalwareMultiScan.Scanner/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS builder - -WORKDIR /src - -COPY MalwareMultiScan.Scanner /src/MalwareMultiScan.Scanner -COPY MalwareMultiScan.Backends /src/MalwareMultiScan.Backends - -RUN dotnet publish -c Release -r linux-x64 -o ./publish MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj - -FROM mcr.microsoft.com/dotnet/core/runtime:3.1 - -WORKDIR /worker - -COPY --from=builder /src/publish /worker - -ENTRYPOINT ["/worker/MalwareMultiScan.Scanner"] \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj b/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj deleted file mode 100644 index b904dcc..0000000 --- a/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - netcoreapp3.1 - Volodymyr Smirnov - MalwareMultiScan Scanner - 1.0.1 - 1.0.1 - true - - - - - - - - - - - - diff --git a/MalwareMultiScan.Scanner/Program.cs b/MalwareMultiScan.Scanner/Program.cs deleted file mode 100644 index 8fc6e45..0000000 --- a/MalwareMultiScan.Scanner/Program.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks; -using Hangfire; -using Hangfire.MemoryStorage; -using MalwareMultiScan.Backends.Extensions; -using MalwareMultiScan.Scanner.Services.Implementations; -using MalwareMultiScan.Scanner.Services.Interfaces; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace MalwareMultiScan.Scanner -{ - [ExcludeFromCodeCoverage] - internal static class Program - { - public static async Task Main(string[] args) - { - await Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(configure => - { - configure.AddJsonFile("appsettings.json"); - configure.AddEnvironmentVariables(); - }) - .ConfigureServices((context, services) => - { - services.AddLogging(); - - services.AddRabbitMq(context.Configuration); - services.AddScanningBackend(context.Configuration); - - services.AddSingleton(); - services.AddHostedService(); - - services.AddHangfire( - configuration => configuration.UseMemoryStorage()); - - services.AddHangfireServer(options => - { - options.WorkerCount = context.Configuration.GetValue("WorkerCount"); - }); - }).RunConsoleAsync(); - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/Services/Implementations/ScanHostedService.cs b/MalwareMultiScan.Scanner/Services/Implementations/ScanHostedService.cs deleted file mode 100644 index 180249a..0000000 --- a/MalwareMultiScan.Scanner/Services/Implementations/ScanHostedService.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using EasyNetQ; -using Hangfire; -using MalwareMultiScan.Backends.Interfaces; -using MalwareMultiScan.Backends.Messages; -using MalwareMultiScan.Scanner.Services.Interfaces; -using Microsoft.Extensions.Logging; - -namespace MalwareMultiScan.Scanner.Services.Implementations -{ - /// - public class ScanHostedService : IScanHostedService - { - private readonly IScanBackend _backend; - private readonly IBus _bus; - private readonly ILogger _logger; - - /// - /// Initialise scan hosted service. - /// - /// Logger. - /// Scan backend. - /// EasyNetQ bus. - public ScanHostedService( - ILogger logger, - IScanBackend backend, IBus bus) - { - _logger = logger; - _bus = bus; - _backend = backend; - } - - /// - public Task StartAsync(CancellationToken cancellationToken) - { - _bus.Receive(_backend.Id, message => - BackgroundJob.Enqueue(j => j.Process(message))); - - _logger.LogInformation( - $"Started scan hosting service for the backend {_backend.Id}"); - - return Task.CompletedTask; - } - - /// - public Task StopAsync(CancellationToken cancellationToken) - { - _bus.Dispose(); - - _logger.LogInformation( - $"Stopped scan hosting service for the backend {_backend.Id}"); - - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/Services/Interfaces/IScanBackgroundJob.cs b/MalwareMultiScan.Scanner/Services/Interfaces/IScanBackgroundJob.cs deleted file mode 100644 index e688dc9..0000000 --- a/MalwareMultiScan.Scanner/Services/Interfaces/IScanBackgroundJob.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Threading.Tasks; -using MalwareMultiScan.Backends.Messages; - -namespace MalwareMultiScan.Scanner.Services.Interfaces -{ - /// - /// Background scanning job. - /// - public interface IScanBackgroundJob - { - /// - /// Process scan request in the background worker. - /// - /// Request message. - Task Process(ScanRequestMessage requestMessage); - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/Services/Interfaces/IScanHostedService.cs b/MalwareMultiScan.Scanner/Services/Interfaces/IScanHostedService.cs deleted file mode 100644 index 8286292..0000000 --- a/MalwareMultiScan.Scanner/Services/Interfaces/IScanHostedService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Extensions.Hosting; - -namespace MalwareMultiScan.Scanner.Services.Interfaces -{ - /// - /// Scan hosted service. - /// - public interface IScanHostedService : IHostedService - { - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/appsettings.json b/MalwareMultiScan.Scanner/appsettings.json deleted file mode 100644 index 55e4b45..0000000 --- a/MalwareMultiScan.Scanner/appsettings.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - - "BackendType": "Dummy", - - "MaxScanningTime": 60, - "ResultsSubscriptionId": "mms.results", - "WorkerCount": 4, - - "ConnectionStrings": { - "RabbitMQ": "host=localhost;prefetchcount=1" - } -} diff --git a/MalwareMultiScan.ScannerWorker/MalwareMultiScan.ScannerWorker.csproj b/MalwareMultiScan.ScannerWorker/MalwareMultiScan.ScannerWorker.csproj new file mode 100644 index 0000000..b69ce1f --- /dev/null +++ b/MalwareMultiScan.ScannerWorker/MalwareMultiScan.ScannerWorker.csproj @@ -0,0 +1,15 @@ + + + netcoreapp3.1 + + + + + + + + + + + + diff --git a/MalwareMultiScan.ScannerWorker/Program.cs b/MalwareMultiScan.ScannerWorker/Program.cs new file mode 100644 index 0000000..d56c958 --- /dev/null +++ b/MalwareMultiScan.ScannerWorker/Program.cs @@ -0,0 +1,50 @@ +using System.Threading.Tasks; +using Hangfire; +using MalwareMultiScan.Backends.Backends.Interfaces; +using MalwareMultiScan.Backends.Extensions; +using MalwareMultiScan.Backends.Services.Implementations; +using MalwareMultiScan.Backends.Services.Interfaces; +using MalwareMultiScan.ScannerWorker.Services; +using MalwareMultiScan.Shared.Extensions; +using MalwareMultiScan.Shared.Services.Interfaces; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace MalwareMultiScan.ScannerWorker +{ + public static class Program + { + public static async Task Main(string[] args) + { + await Host.CreateDefaultBuilder(args) + .ConfigureLogging((context, builder) => + { + builder.AddConsole(); + builder.AddConfiguration(context.Configuration); + }) + .ConfigureAppConfiguration(builder => + { + builder.AddJsonFile("appsettings.json"); + builder.AddEnvironmentVariables(); + }) + .ConfigureServices((context, services) => + { + services.AddConsul(context.Configuration); + services.AddScanBackend(context.Configuration); + + services.AddHangfire(context.Configuration, + context.Configuration.GetValue("BACKEND_ID")); + + services.AddSingleton(); + services.AddSingleton(); + + services.AddHostedService(); + }) + .UseConsoleLifetime() + .Build() + .RunAsync(); + } + } +} \ No newline at end of file diff --git a/MalwareMultiScan.ScannerWorker/Properties/launchSettings.json b/MalwareMultiScan.ScannerWorker/Properties/launchSettings.json new file mode 100644 index 0000000..bd14be3 --- /dev/null +++ b/MalwareMultiScan.ScannerWorker/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "MalwareMultiScan.ScannerWorker": { + "commandName": "Project" + } + } +} diff --git a/MalwareMultiScan.ScannerWorker/Services/ConsulHostedService.cs b/MalwareMultiScan.ScannerWorker/Services/ConsulHostedService.cs new file mode 100644 index 0000000..0f45532 --- /dev/null +++ b/MalwareMultiScan.ScannerWorker/Services/ConsulHostedService.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Consul; +using MalwareMultiScan.Backends.Backends.Interfaces; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace MalwareMultiScan.ScannerWorker.Services +{ + public class ConsulHostedService : BackgroundService + { + private static readonly string ServiceId = Dns.GetHostName(); + private static readonly string CheckId = $"ttl-{ServiceId}"; + + private readonly IConsulClient _consulClient; + private readonly AgentServiceRegistration _registration; + + public ConsulHostedService( + IConsulClient consulClient, + IConfiguration configuration, + IScanBackend scanBackend) + { + _consulClient = consulClient; + + _registration = new AgentServiceRegistration + { + ID = ServiceId, + Name = configuration.GetValue("CONSUL_SERVICE_NAME"), + + Meta = new Dictionary + { + {"BackendId", scanBackend.Id} + } + }; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + await _consulClient.Agent.PassTTL( + CheckId, string.Empty, stoppingToken); + + await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); + } + } + + public override async Task StartAsync(CancellationToken cancellationToken) + { + await _consulClient.Agent.ServiceDeregister( + _registration.ID, cancellationToken); + + await _consulClient.Agent.ServiceRegister( + _registration, cancellationToken); + + await _consulClient.Agent.CheckRegister(new AgentCheckRegistration + { + ID = CheckId, + Name = "TTL", + ServiceID = ServiceId, + TTL = TimeSpan.FromSeconds(15), + DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(60) + }, cancellationToken); + + await base.StartAsync(cancellationToken); + } + + public override async Task StopAsync(CancellationToken cancellationToken) + { + await _consulClient.Agent.ServiceDeregister( + _registration.ID, cancellationToken); + + await _consulClient.Agent.CheckDeregister( + CheckId, cancellationToken); + + await base.StopAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/Services/Implementations/ScanBackgroundJob.cs b/MalwareMultiScan.ScannerWorker/Services/ScanBackgroundJob.cs similarity index 55% rename from MalwareMultiScan.Scanner/Services/Implementations/ScanBackgroundJob.cs rename to MalwareMultiScan.ScannerWorker/Services/ScanBackgroundJob.cs index 0804298..7f16f91 100644 --- a/MalwareMultiScan.Scanner/Services/Implementations/ScanBackgroundJob.cs +++ b/MalwareMultiScan.ScannerWorker/Services/ScanBackgroundJob.cs @@ -2,53 +2,56 @@ using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Backends.Interfaces; -using MalwareMultiScan.Backends.Messages; -using MalwareMultiScan.Scanner.Services.Interfaces; +using Hangfire; +using Hangfire.States; +using MalwareMultiScan.Backends.Backends.Interfaces; +using MalwareMultiScan.Shared.Enums; +using MalwareMultiScan.Shared.Message; +using MalwareMultiScan.Shared.Services.Interfaces; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -namespace MalwareMultiScan.Scanner.Services.Implementations +namespace MalwareMultiScan.ScannerWorker.Services { /// public class ScanBackgroundJob : IScanBackgroundJob { private readonly IScanBackend _backend; - private readonly IBus _bus; private readonly IConfiguration _configuration; + private readonly IBackgroundJobClient _jobClient; private readonly ILogger _logger; /// /// Initialize scan background job. /// /// Configuration. - /// Bus. /// Logger. /// Scan backend. - public ScanBackgroundJob(IConfiguration configuration, IBus bus, - ILogger logger, IScanBackend backend) + /// + public ScanBackgroundJob( + IScanBackend backend, + IConfiguration configuration, + ILogger logger, + IBackgroundJobClient jobClient) { - _configuration = configuration; - _bus = bus; - _logger = logger; _backend = backend; + _configuration = configuration; + _logger = logger; + _jobClient = jobClient; } /// - public async Task Process(ScanRequestMessage message) + public async Task Process(ScanQueueMessage message) { _logger.LogInformation( $"Starting scan of {message.Uri} via backend {_backend.Id} from {message.Id}"); var cancellationTokenSource = new CancellationTokenSource( - TimeSpan.FromSeconds(_configuration.GetValue("MaxScanningTime"))); + TimeSpan.FromSeconds(_configuration.GetValue("MAX_SCANNING_TIME"))); - var result = new ScanResultMessage - { - Id = message.Id, - Backend = _backend.Id - }; + var cancellationToken = cancellationTokenSource.Token; + + var result = new ScanResultMessage(); var stopwatch = new Stopwatch(); @@ -56,10 +59,9 @@ namespace MalwareMultiScan.Scanner.Services.Implementations try { - result.Threats = await _backend.ScanAsync( - message.Uri, cancellationTokenSource.Token); + result.Threats = await _backend.ScanAsync(message.Uri, cancellationToken); - result.Succeeded = true; + result.Status = ScanResultStatus.Succeeded; _logger.LogInformation( $"Backend {_backend.Id} completed a scan of {message.Id} " + @@ -67,7 +69,7 @@ namespace MalwareMultiScan.Scanner.Services.Implementations } catch (Exception exception) { - result.Succeeded = false; + result.Status = ScanResultStatus.Failed; _logger.LogError( exception, "Scanning failed with exception"); @@ -79,11 +81,18 @@ namespace MalwareMultiScan.Scanner.Services.Implementations result.Duration = stopwatch.ElapsedMilliseconds / 1000; - _logger.LogInformation( - $"Sending scan results with status {result.Succeeded}"); + try + { + _logger.LogInformation( + $"Sending scan results with status {result.Status}"); - await _bus.SendAsync( - _configuration.GetValue("ResultsSubscriptionId"), result); + _jobClient.Create( + x => x.Report(message.Id, _backend.Id, result), new EnqueuedState()); + } + catch (Exception exception) + { + _logger.LogError(exception, "Failed to send scan results"); + } } } } \ No newline at end of file diff --git a/MalwareMultiScan.ScannerWorker/appsettings.json b/MalwareMultiScan.ScannerWorker/appsettings.json new file mode 100644 index 0000000..5f5379c --- /dev/null +++ b/MalwareMultiScan.ScannerWorker/appsettings.json @@ -0,0 +1,9 @@ +{ + "REDIS_ADDRESS": "localhost:6379", + + "CONSUL_ADDRESS": "http://localhost:8500", + "CONSUL_SERVICE_NAME": "scanner", + + "BACKEND_ID": "dummy", + "MAX_SCANNING_TIME": 60 +} diff --git a/MalwareMultiScan.Shared/Enums/ScanResultStatus.cs b/MalwareMultiScan.Shared/Enums/ScanResultStatus.cs new file mode 100644 index 0000000..f3b9178 --- /dev/null +++ b/MalwareMultiScan.Shared/Enums/ScanResultStatus.cs @@ -0,0 +1,9 @@ +namespace MalwareMultiScan.Shared.Enums +{ + public enum ScanResultStatus + { + Queued, + Succeeded, + Failed + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Extensions/ServiceCollectionExtensions.cs b/MalwareMultiScan.Shared/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..d2fbbf3 --- /dev/null +++ b/MalwareMultiScan.Shared/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,40 @@ +using System; +using Consul; +using Hangfire; +using Hangfire.Redis; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace MalwareMultiScan.Shared.Extensions +{ + /// + /// Extensions for IServiceCollection. + /// + public static class ServiceCollectionExtensions + { + /// + /// Add consul to the service collection and register the node on start. + /// + /// Service collection. + /// Configuration. + public static void AddConsul(this IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton(new ConsulClient(config => + { + config.Address = configuration.GetValue("CONSUL_ADDRESS"); + })); + } + + public static void AddHangfire(this IServiceCollection services, + IConfiguration configuration, params string[] queues) + { + if (queues.Length == 0) + queues = new[] {"default"}; + + services.AddHangfire(options => + options.UseRedisStorage(configuration.GetValue("REDIS_ADDRESS"))); + + services.AddHangfireServer(options => options.Queues = queues); + } + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/MalwareMultiScan.Shared.csproj b/MalwareMultiScan.Shared/MalwareMultiScan.Shared.csproj new file mode 100644 index 0000000..b69f6ae --- /dev/null +++ b/MalwareMultiScan.Shared/MalwareMultiScan.Shared.csproj @@ -0,0 +1,20 @@ + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + diff --git a/MalwareMultiScan.Shared/Message/ScanQueueMessage.cs b/MalwareMultiScan.Shared/Message/ScanQueueMessage.cs new file mode 100644 index 0000000..d1afb1d --- /dev/null +++ b/MalwareMultiScan.Shared/Message/ScanQueueMessage.cs @@ -0,0 +1,10 @@ +using System; + +namespace MalwareMultiScan.Shared.Message +{ + public class ScanQueueMessage + { + public string Id { get; set; } + public Uri Uri { get; set; } + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Message/ScanResultMessage.cs b/MalwareMultiScan.Shared/Message/ScanResultMessage.cs new file mode 100644 index 0000000..8ed6a7a --- /dev/null +++ b/MalwareMultiScan.Shared/Message/ScanResultMessage.cs @@ -0,0 +1,13 @@ +using MalwareMultiScan.Shared.Enums; + +namespace MalwareMultiScan.Shared.Message +{ + public class ScanResultMessage + { + public ScanResultStatus Status { get; set; } + + public long Duration { get; set; } + + public string[] Threats { get; set; } + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Services/Interfaces/IScanBackgroundJob.cs b/MalwareMultiScan.Shared/Services/Interfaces/IScanBackgroundJob.cs new file mode 100644 index 0000000..d552b81 --- /dev/null +++ b/MalwareMultiScan.Shared/Services/Interfaces/IScanBackgroundJob.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using Hangfire; +using MalwareMultiScan.Shared.Message; + +namespace MalwareMultiScan.Shared.Services.Interfaces +{ + [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)] + public interface IScanBackgroundJob + { + Task Process(ScanQueueMessage message); + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Services/Interfaces/IScanResultJob.cs b/MalwareMultiScan.Shared/Services/Interfaces/IScanResultJob.cs new file mode 100644 index 0000000..13c8f02 --- /dev/null +++ b/MalwareMultiScan.Shared/Services/Interfaces/IScanResultJob.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; +using Hangfire; +using MalwareMultiScan.Shared.Message; + +namespace MalwareMultiScan.Shared.Services.Interfaces +{ + public interface IScanResultJob + { + [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)] + Task Report(string resultId, string backendId, ScanResultMessage result); + + [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)] + Task Notify(Uri uri, string resultId, string backendId, ScanResultMessage result); + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Tests/Api/ControllersTests.cs b/MalwareMultiScan.Tests/Api/ControllersTests.cs index 7383af2..4b71718 100644 --- a/MalwareMultiScan.Tests/Api/ControllersTests.cs +++ b/MalwareMultiScan.Tests/Api/ControllersTests.cs @@ -1,7 +1,7 @@ using System.IO; using System.Threading.Tasks; using MalwareMultiScan.Api.Controllers; -using MalwareMultiScan.Api.Data.Models; +using MalwareMultiScan.Api.Data; using MalwareMultiScan.Api.Services.Interfaces; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/MalwareMultiScan.Tests/Api/ReceiverHostedServiceTests.cs b/MalwareMultiScan.Tests/Api/ReceiverHostedServiceTests.cs index a92f897..d248a68 100644 --- a/MalwareMultiScan.Tests/Api/ReceiverHostedServiceTests.cs +++ b/MalwareMultiScan.Tests/Api/ReceiverHostedServiceTests.cs @@ -2,10 +2,7 @@ using System; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Api.Services.Implementations; using MalwareMultiScan.Api.Services.Interfaces; -using MalwareMultiScan.Backends.Messages; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Memory; using Microsoft.Extensions.Logging; diff --git a/MalwareMultiScan.Tests/Api/ScanBackendServiceTests.cs b/MalwareMultiScan.Tests/Api/ScanBackendServiceTests.cs index 8e8fbf2..2632efb 100644 --- a/MalwareMultiScan.Tests/Api/ScanBackendServiceTests.cs +++ b/MalwareMultiScan.Tests/Api/ScanBackendServiceTests.cs @@ -1,12 +1,8 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using EasyNetQ; +using MalwareMultiScan.Api.Data; using MalwareMultiScan.Api.Data.Configuration; -using MalwareMultiScan.Api.Data.Models; -using MalwareMultiScan.Api.Services.Implementations; -using MalwareMultiScan.Api.Services.Interfaces; -using MalwareMultiScan.Backends.Messages; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Memory; using Microsoft.Extensions.Logging; diff --git a/MalwareMultiScan.Tests/Api/ScanResultServiceTests.cs b/MalwareMultiScan.Tests/Api/ScanResultServiceTests.cs index e85cdbb..903f07c 100644 --- a/MalwareMultiScan.Tests/Api/ScanResultServiceTests.cs +++ b/MalwareMultiScan.Tests/Api/ScanResultServiceTests.cs @@ -1,8 +1,8 @@ using System.IO; using System.Threading.Tasks; +using MalwareMultiScan.Api.Data; using MalwareMultiScan.Api.Data.Configuration; -using MalwareMultiScan.Api.Data.Models; -using MalwareMultiScan.Api.Services.Implementations; +using MalwareMultiScan.Api.Services; using MalwareMultiScan.Api.Services.Interfaces; using Mongo2Go; using MongoDB.Bson; diff --git a/MalwareMultiScan.Tests/Backends/BackendsTests.cs b/MalwareMultiScan.Tests/Backends/BackendsTests.cs index 619da9a..aa22a03 100644 --- a/MalwareMultiScan.Tests/Backends/BackendsTests.cs +++ b/MalwareMultiScan.Tests/Backends/BackendsTests.cs @@ -1,7 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using MalwareMultiScan.Backends.Backends.Implementations; -using MalwareMultiScan.Backends.Interfaces; +using MalwareMultiScan.Backends.Backends; using MalwareMultiScan.Backends.Services.Interfaces; using Moq; using NUnit.Framework; diff --git a/MalwareMultiScan.Tests/Scanner/ScanBackgroundJobTests.cs b/MalwareMultiScan.Tests/Scanner/ScanBackgroundJobTests.cs index d255d03..63f5749 100644 --- a/MalwareMultiScan.Tests/Scanner/ScanBackgroundJobTests.cs +++ b/MalwareMultiScan.Tests/Scanner/ScanBackgroundJobTests.cs @@ -1,13 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Backends.Interfaces; -using MalwareMultiScan.Backends.Messages; -using MalwareMultiScan.Scanner.Services.Implementations; -using MalwareMultiScan.Scanner.Services.Interfaces; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Memory; using Microsoft.Extensions.Logging; diff --git a/MalwareMultiScan.Tests/Scanner/ScanHostedServiceTests.cs b/MalwareMultiScan.Tests/Scanner/ScanHostedServiceTests.cs index a7a2a19..fbb125c 100644 --- a/MalwareMultiScan.Tests/Scanner/ScanHostedServiceTests.cs +++ b/MalwareMultiScan.Tests/Scanner/ScanHostedServiceTests.cs @@ -1,10 +1,5 @@ using System; using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Backends.Interfaces; -using MalwareMultiScan.Backends.Messages; -using MalwareMultiScan.Scanner.Services.Implementations; -using MalwareMultiScan.Scanner.Services.Interfaces; using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; diff --git a/MalwareMultiScan.Ui/models/scan-result-entry-flattened.ts b/MalwareMultiScan.Ui/models/scan-result-entry-flattened.ts index faa2df9..56be49c 100644 --- a/MalwareMultiScan.Ui/models/scan-result-entry-flattened.ts +++ b/MalwareMultiScan.Ui/models/scan-result-entry-flattened.ts @@ -1,19 +1,31 @@ import ScanResultEntry from '~/models/scan-result-entry'; +import {ScanResultStatus} from '~/models/scan-result-status'; export default class ScanResultEntryFlattened implements ScanResultEntry { readonly id: string; - readonly completed: boolean; - readonly succeeded: boolean | null; + readonly status: ScanResultStatus; readonly duration: number; readonly threats: string[]; - constructor(id: string, completed: boolean, succeeded: boolean | null, duration: number, threats: string[]) { + constructor(id: string, status: ScanResultStatus, duration: number, threats: string[]) { this.id = id; - this.completed = completed; - this.succeeded = succeeded; + this.status = status; this.duration = duration; this.threats = threats; } + get completed() + { + return this.status != ScanResultStatus.Queued + } + get succeeded() + { + return this.status === ScanResultStatus.Succeeded; + } + + get failed() + { + return this.status === ScanResultStatus.Failed; + } } diff --git a/MalwareMultiScan.Ui/models/scan-result-entry.ts b/MalwareMultiScan.Ui/models/scan-result-entry.ts index 96856b3..0cf0767 100644 --- a/MalwareMultiScan.Ui/models/scan-result-entry.ts +++ b/MalwareMultiScan.Ui/models/scan-result-entry.ts @@ -1,6 +1,7 @@ +import { ScanResultStatus } from '~/models/scan-result-status'; + export default interface ScanResultEntry { - readonly completed: boolean, - readonly succeeded: boolean | null, + readonly status: ScanResultStatus, readonly duration: number, readonly threats: string[] } diff --git a/MalwareMultiScan.Ui/pages/_id/index.vue b/MalwareMultiScan.Ui/pages/_id/index.vue index 509cb76..b85fc3a 100644 --- a/MalwareMultiScan.Ui/pages/_id/index.vue +++ b/MalwareMultiScan.Ui/pages/_id/index.vue @@ -1,9 +1,9 @@