diff --git a/server/AyaNova/Controllers/BackupController.cs b/server/AyaNova/Controllers/BackupController.cs
index 7cefd639..d605b309 100644
--- a/server/AyaNova/Controllers/BackupController.cs
+++ b/server/AyaNova/Controllers/BackupController.cs
@@ -7,10 +7,9 @@ using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
using System.Threading.Tasks;
-using AyaNova.Biz;
-using Newtonsoft.Json.Linq;
using AyaNova.Util;
-using System.Linq;
+using System;
+using Microsoft.AspNetCore.Http;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************** */
@@ -103,6 +102,53 @@ namespace AyaNova.Api.Controllers
return Ok(ApiOkResponse.Response(FileUtil.UtilityFileList()));
}
+ ///
+ /// Download a backup file
+ ///
+ ///
+ /// download token
+ ///
+ [HttpGet("download/{fileName}")]
+ public async Task DownloadAsync([FromRoute] string fileName, [FromQuery] string t)
+ {
+ int nFailedAuthDelay = 3000;
+ if (!serverState.IsOpen)
+ return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
+ if (string.IsNullOrWhiteSpace(t))
+ {
+ await Task.Delay(nFailedAuthDelay);//DOS protection
+ return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
+ }
+ var DownloadUser = await ct.User.AsNoTracking().SingleOrDefaultAsync(z => z.DlKey == t && z.Active == true);
+ if (DownloadUser == null)
+ {
+ await Task.Delay(nFailedAuthDelay);//DOS protection
+ return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
+ }
+ var utcNow = new DateTimeOffset(DateTime.Now.ToUniversalTime(), TimeSpan.Zero);
+ if (DownloadUser.DlKeyExpire < utcNow.DateTime)
+ {
+ await Task.Delay(nFailedAuthDelay);//DOS protection
+ return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
+ }
+ if (!Authorized.HasAnyRole(HttpContext.Items, AuthorizationRoles.OpsAdminFull | AuthorizationRoles.OpsAdminLimited))
+ {
+ await Task.Delay(nFailedAuthDelay);//DOS protection
+ return StatusCode(403, new ApiNotAuthorizedResponse());
+ }
+
+ var AvailableFiles = FileUtil.UtilityFileList();
+ if (!AvailableFiles.Contains(fileName))
+ {
+ await Task.Delay(nFailedAuthDelay);//fishing protection
+ return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
+ }
+ string mimetype = fileName.EndsWith("zip") ? "application/zip" : "application/octet-stream";
+ var utilityFilePath = FileUtil.GetFullPathForUtilityFile(fileName);
+ await EventLogProcessor.LogEventToDatabaseAsync(new Event(DownloadUser.Id, 0, AyaType.NoType, AyaEvent.UtilityFileDownload, fileName), ct);
+ return PhysicalFile(utilityFilePath, mimetype);
+
+ }
}//eoc
diff --git a/server/AyaNova/biz/AyaEvent.cs b/server/AyaNova/biz/AyaEvent.cs
index 39a1c517..deaf12bd 100644
--- a/server/AyaNova/biz/AyaEvent.cs
+++ b/server/AyaNova/biz/AyaEvent.cs
@@ -15,17 +15,18 @@ namespace AyaNova.Biz
Modified = 3,
//specific events
- AttachmentCreate=4,
- AttachmentDelete=5,
- AttachmentDownload=6,
-
- LicenseFetch=7,
- LicenseTrialRequest=8,
- ServerStateChange=9,
- SeedDatabase=10,
- AttachmentModified=11,
- EraseAllData=12,
- ResetSerial=13
+ AttachmentCreate = 4,
+ AttachmentDelete = 5,
+ AttachmentDownload = 6,
+
+ LicenseFetch = 7,
+ LicenseTrialRequest = 8,
+ ServerStateChange = 9,
+ SeedDatabase = 10,
+ AttachmentModified = 11,
+ EraseAllData = 12,
+ ResetSerial = 13,
+ UtilityFileDownload = 14
//NEW ITEMS REQUIRE translation KEYS and update CLIENT ay-history.vue code in eventypes list and translation fetcher