Skip to content
This repository was archived by the owner on May 17, 2021. It is now read-only.

Commit 3f49c30

Browse files
Merge pull request #20 from mindcollapse/v1.1
Multiple improvements for version 1.1
2 parents 89fb0b3 + ee07181 commit 3f49c30

31 files changed

Lines changed: 346 additions & 149 deletions

MalwareMultiScan.Api/Attributes/IsHttpUrlAttribute.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,23 @@ namespace MalwareMultiScan.Api.Attributes
88
/// </summary>
99
public class IsHttpUrlAttribute : ValidationAttribute
1010
{
11+
private readonly bool _failOnNull;
12+
13+
/// <summary>
14+
/// Initialize http URL validation attribute.
15+
/// </summary>
16+
/// <param name="failOnNull">True if URL is mandatory, false otherwise.</param>
17+
public IsHttpUrlAttribute(bool failOnNull = true)
18+
{
19+
_failOnNull = failOnNull;
20+
}
21+
1122
/// <inheritdoc />
1223
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
1324
{
25+
if (!_failOnNull && string.IsNullOrEmpty(value.ToString()))
26+
return ValidationResult.Success;
27+
1428
if (!Uri.TryCreate((string) value, UriKind.Absolute, out var uri)
1529
|| !uri.IsAbsoluteUri
1630
|| uri.Scheme.ToLowerInvariant() != "http"

MalwareMultiScan.Api/Controllers/QueueController.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.ComponentModel.DataAnnotations;
23
using System.Threading.Tasks;
34
using MalwareMultiScan.Api.Attributes;
@@ -31,13 +32,16 @@ public QueueController(IScanResultService scanResultService)
3132
/// Queue file for scanning.
3233
/// </summary>
3334
/// <param name="file">File from form data.</param>
35+
/// <param name="callbackUrl">Optional callback URL.</param>
3436
[HttpPost("file")]
3537
[ProducesResponseType(typeof(ScanResult), StatusCodes.Status201Created)]
3638
[ProducesResponseType(StatusCodes.Status400BadRequest)]
3739
public async Task<IActionResult> ScanFile(
38-
[Required] [MaxFileSize] IFormFile file)
40+
[Required] [MaxFileSize] IFormFile file,
41+
[FromForm] [IsHttpUrl(false)] string callbackUrl = null)
3942
{
40-
var result = await _scanResultService.CreateScanResult();
43+
var result = await _scanResultService.CreateScanResult(
44+
string.IsNullOrEmpty(callbackUrl) ? null : new Uri(callbackUrl));
4145

4246
string storedFileId;
4347

@@ -56,13 +60,16 @@ public async Task<IActionResult> ScanFile(
5660
/// Queue URL for scanning.
5761
/// </summary>
5862
/// <param name="url">URL from form data.</param>
63+
/// <param name="callbackUrl">Optional callback URL.</param>
5964
[HttpPost("url")]
6065
[ProducesResponseType(typeof(ScanResult), StatusCodes.Status201Created)]
6166
[ProducesResponseType(StatusCodes.Status400BadRequest)]
6267
public async Task<IActionResult> ScanUrl(
63-
[FromForm] [Required] [IsHttpUrl] string url)
68+
[FromForm] [Required] [IsHttpUrl] string url,
69+
[FromForm] [IsHttpUrl(false)] string callbackUrl = null)
6470
{
65-
var result = await _scanResultService.CreateScanResult();
71+
var result = await _scanResultService.CreateScanResult(
72+
string.IsNullOrEmpty(callbackUrl) ? null : new Uri(callbackUrl));
6673

6774
await _scanResultService.QueueUrlScan(result, url);
6875

MalwareMultiScan.Api/Controllers/ScanResultsController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace MalwareMultiScan.Api.Controllers
1515
public class ScanResultsController : Controller
1616
{
1717
private readonly IScanResultService _scanResultService;
18-
18+
1919
/// <summary>
2020
/// Initialize scan results controller.
2121
/// </summary>

MalwareMultiScan.Api/Data/Configuration/ScanBackend.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class ScanBackend
99
/// Backend id.
1010
/// </summary>
1111
public string Id { get; set; }
12-
12+
1313
/// <summary>
1414
/// Backend state.
1515
/// </summary>

MalwareMultiScan.Api/Data/Models/ScanResult.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using MongoDB.Bson;
34
using MongoDB.Bson.Serialization.Attributes;
@@ -17,7 +18,12 @@ public class ScanResult
1718
public string Id { get; set; }
1819

1920
/// <summary>
20-
/// Result entries where key is backend id and value is <see cref="ScanResultEntry"/>.
21+
/// Optional callback URL.
22+
/// </summary>
23+
public Uri CallbackUrl { get; set; }
24+
25+
/// <summary>
26+
/// Result entries where key is backend id and value is <see cref="ScanResultEntry" />.
2127
/// </summary>
2228
public Dictionary<string, ScanResultEntry> Results { get; set; } =
2329
new Dictionary<string, ScanResultEntry>();

MalwareMultiScan.Api/Data/Models/ScanResultEntry.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@ public class ScanResultEntry
99
/// Completion status.
1010
/// </summary>
1111
public bool Completed { get; set; }
12-
12+
1313
/// <summary>
1414
/// Indicates that scanning completed without error.
1515
/// </summary>
1616
public bool? Succeeded { get; set; }
17-
17+
1818
/// <summary>
1919
/// Scanning duration in seconds.
2020
/// </summary>
2121
public long Duration { get; set; }
22-
22+
2323
/// <summary>
2424
/// Detected names of threats.
2525
/// </summary>

MalwareMultiScan.Api/Services/Implementations/ReceiverHostedService.cs

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
using System;
2+
using System.Net.Http;
3+
using System.Text;
14
using System.Threading;
25
using System.Threading.Tasks;
36
using EasyNetQ;
47
using MalwareMultiScan.Api.Services.Interfaces;
58
using MalwareMultiScan.Backends.Messages;
69
using Microsoft.Extensions.Configuration;
710
using Microsoft.Extensions.Logging;
11+
using Newtonsoft.Json;
812

913
namespace MalwareMultiScan.Api.Services.Implementations
1014
{
@@ -13,6 +17,7 @@ public class ReceiverHostedService : IReceiverHostedService
1317
{
1418
private readonly IBus _bus;
1519
private readonly IConfiguration _configuration;
20+
private readonly IHttpClientFactory _httpClientFactory;
1621
private readonly ILogger<ReceiverHostedService> _logger;
1722
private readonly IScanResultService _scanResultService;
1823

@@ -23,31 +28,23 @@ public class ReceiverHostedService : IReceiverHostedService
2328
/// <param name="configuration">Configuration.</param>
2429
/// <param name="scanResultService">Scan result service.</param>
2530
/// <param name="logger">Logger.</param>
31+
/// <param name="httpClientFactory">HTTP client factory.</param>
2632
public ReceiverHostedService(IBus bus, IConfiguration configuration, IScanResultService scanResultService,
27-
ILogger<ReceiverHostedService> logger)
33+
ILogger<ReceiverHostedService> logger, IHttpClientFactory httpClientFactory)
2834
{
2935
_bus = bus;
3036
_configuration = configuration;
3137
_scanResultService = scanResultService;
3238
_logger = logger;
39+
_httpClientFactory = httpClientFactory;
3340
}
3441

3542

3643
/// <inheritdoc />
3744
public Task StartAsync(CancellationToken cancellationToken)
3845
{
39-
_bus.Receive<ScanResultMessage>(_configuration.GetValue<string>("ResultsSubscriptionId"), async message =>
40-
{
41-
message.Threats ??= new string[] { };
42-
43-
_logger.LogInformation(
44-
$"Received a result from {message.Backend} for {message.Id} " +
45-
$"with threats {string.Join(",", message.Threats)}");
46-
47-
await _scanResultService.UpdateScanResultForBackend(
48-
message.Id, message.Backend, message.Duration, true,
49-
message.Succeeded, message.Threats);
50-
});
46+
_bus.Receive<ScanResultMessage>(
47+
_configuration.GetValue<string>("ResultsSubscriptionId"), StoreScanResult);
5148

5249
_logger.LogInformation(
5350
"Started hosted service for receiving scan results");
@@ -65,5 +62,42 @@ public Task StopAsync(CancellationToken cancellationToken)
6562

6663
return Task.CompletedTask;
6764
}
65+
66+
private async Task StoreScanResult(ScanResultMessage message)
67+
{
68+
message.Threats ??= new string[] { };
69+
70+
_logger.LogInformation(
71+
$"Received a result from {message.Backend} for {message.Id} " +
72+
$"with threats {string.Join(",", message.Threats)}");
73+
74+
await _scanResultService.UpdateScanResultForBackend(
75+
message.Id, message.Backend, message.Duration, true,
76+
message.Succeeded, message.Threats);
77+
78+
var result = await _scanResultService.GetScanResult(message.Id);
79+
80+
if (result?.CallbackUrl == null)
81+
return;
82+
83+
var cancellationTokenSource = new CancellationTokenSource(
84+
TimeSpan.FromSeconds(3));
85+
86+
using var httpClient = _httpClientFactory.CreateClient();
87+
88+
try
89+
{
90+
var response = await httpClient.PostAsync(
91+
result.CallbackUrl,
92+
new StringContent(JsonConvert.SerializeObject(result), Encoding.UTF8, "application/json"),
93+
cancellationTokenSource.Token);
94+
95+
response.EnsureSuccessStatusCode();
96+
}
97+
catch (Exception exception)
98+
{
99+
_logger.LogError(exception, $"Failed to POST to callback URL {result.CallbackUrl}");
100+
}
101+
}
68102
}
69103
}

MalwareMultiScan.Api/Services/Implementations/ScanResultService.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.IO;
23
using System.Linq;
34
using System.Threading.Tasks;
@@ -31,12 +32,14 @@ public ScanResultService(IMongoDatabase db, IGridFSBucket bucket, IScanBackendSe
3132

3233
_collection = db.GetCollection<ScanResult>(CollectionName);
3334
}
34-
35+
3536
/// <inheritdoc />
36-
public async Task<ScanResult> CreateScanResult()
37+
public async Task<ScanResult> CreateScanResult(Uri callbackUrl)
3738
{
3839
var scanResult = new ScanResult
3940
{
41+
CallbackUrl = callbackUrl,
42+
4043
Results = _scanBackendService.List
4144
.Where(b => b.Enabled)
4245
.ToDictionary(k => k.Id, v => new ScanResultEntry())

MalwareMultiScan.Api/Services/Interfaces/IScanResultService.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.IO;
23
using System.Threading.Tasks;
34
using MalwareMultiScan.Api.Data.Models;
@@ -12,8 +13,9 @@ public interface IScanResultService
1213
/// <summary>
1314
/// Create scan result.
1415
/// </summary>
16+
/// <param name="callbackUrl">Optional callback URL.</param>
1517
/// <returns>Scan result entry with id.</returns>
16-
Task<ScanResult> CreateScanResult();
18+
Task<ScanResult> CreateScanResult(Uri callbackUrl);
1719

1820
/// <summary>
1921
/// Get scan result.

MalwareMultiScan.Api/Startup.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public void ConfigureServices(IServiceCollection services)
3838
services.AddControllers();
3939

4040
services.AddHostedService<ReceiverHostedService>();
41+
42+
services.AddHttpClient();
4143
}
4244

4345
public void Configure(IApplicationBuilder app)

0 commit comments

Comments
 (0)