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