Implemented download in the file manager. Made file access jwt more modular
This commit is contained in:
parent
26ed50c94b
commit
423616b9f3
|
@ -0,0 +1,57 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.Core.Services.Utils;
|
||||
using Moonlight.Features.FileManager.Services;
|
||||
|
||||
namespace Moonlight.Features.FileManager.Http.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/download")]
|
||||
public class DownloadController : Controller
|
||||
{
|
||||
private readonly JwtService JwtService;
|
||||
private readonly SharedFileAccessService SharedFileAccessService;
|
||||
|
||||
public DownloadController(JwtService jwtService, SharedFileAccessService sharedFileAccessService)
|
||||
{
|
||||
JwtService = jwtService;
|
||||
SharedFileAccessService = sharedFileAccessService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult> Upload([FromQuery(Name = "token")] string downloadToken, [FromQuery(Name = "name")] string name)
|
||||
{
|
||||
if (name.Contains(".."))
|
||||
{
|
||||
Logger.Warn($"A user tried to access a file via path transversal. Name: {name}");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// Validate request
|
||||
if (!await JwtService.Validate(downloadToken, "FileAccess"))
|
||||
return StatusCode(403);
|
||||
|
||||
var downloadContext = await JwtService.Decode(downloadToken);
|
||||
|
||||
if (!downloadContext.ContainsKey("FileAccessId"))
|
||||
return BadRequest();
|
||||
|
||||
if (!int.TryParse(downloadContext["FileAccessId"], out int fileAccessId))
|
||||
return BadRequest();
|
||||
|
||||
// Load file access for this file
|
||||
var fileAccess = await SharedFileAccessService.Get(fileAccessId);
|
||||
|
||||
if (fileAccess == null)
|
||||
return BadRequest("Invalid file access id");
|
||||
|
||||
var files = await fileAccess.List();
|
||||
|
||||
if (files.All(x => !x.IsFile && x.Name != name))
|
||||
return NotFound();
|
||||
|
||||
var stream = await fileAccess.ReadFileStream(name);
|
||||
|
||||
return File(stream, "application/octet-stream", name);
|
||||
}
|
||||
}
|
|
@ -45,7 +45,7 @@ public class UploadController : Controller
|
|||
return BadRequest("Too many files sent");
|
||||
|
||||
// Validate request
|
||||
if (!await JwtService.Validate(uploadToken, "FileUpload"))
|
||||
if (!await JwtService.Validate(uploadToken, "FileAccess"))
|
||||
return StatusCode(403);
|
||||
|
||||
var uploadContext = await JwtService.Decode(uploadToken);
|
||||
|
|
|
@ -18,7 +18,10 @@ public class SharedFileAccessService
|
|||
public Task<int> Register(IFileAccess fileAccess)
|
||||
{
|
||||
lock (FileAccesses)
|
||||
FileAccesses.Add(fileAccess);
|
||||
{
|
||||
if(!FileAccesses.Contains(fileAccess))
|
||||
FileAccesses.Add(fileAccess);
|
||||
}
|
||||
|
||||
return Task.FromResult(fileAccess.GetHashCode());
|
||||
}
|
||||
|
@ -47,13 +50,13 @@ public class SharedFileAccessService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<string> GenerateUrl(IFileAccess fileAccess)
|
||||
public async Task<string> GenerateToken(IFileAccess fileAccess)
|
||||
{
|
||||
var token = await JwtService.Create(data =>
|
||||
{
|
||||
data.Add("FileAccessId", fileAccess.GetHashCode().ToString());
|
||||
}, "FileUpload", TimeSpan.FromMinutes(6));
|
||||
|
||||
return $"/api/upload?token={token}";
|
||||
}, "FileAccess", TimeSpan.FromMinutes(6));
|
||||
|
||||
return token;
|
||||
}
|
||||
}
|
|
@ -64,7 +64,9 @@
|
|||
if (firstRender)
|
||||
{
|
||||
await SharedFileAccessService.Register(FileAccess);
|
||||
var url = await SharedFileAccessService.GenerateUrl(FileAccess);
|
||||
|
||||
var token = await SharedFileAccessService.GenerateToken(FileAccess);
|
||||
var url = $"/api/upload?token={token}";
|
||||
|
||||
await DropzoneService.Create(DropzoneId, url);
|
||||
|
||||
|
@ -74,7 +76,8 @@
|
|||
{
|
||||
await Task.Delay(TimeSpan.FromMinutes(5));
|
||||
|
||||
var newUrl = await SharedFileAccessService.GenerateUrl(FileAccess);
|
||||
var newToken = await SharedFileAccessService.GenerateToken(FileAccess);
|
||||
var newUrl = $"/api/upload?token={newToken}";
|
||||
await DropzoneService.UpdateUrl(DropzoneId, newUrl);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
@using Moonlight.Features.FileManager.Models.Abstractions.FileAccess
|
||||
@using MoonCoreUI.Services
|
||||
@using MoonCore.Helpers
|
||||
@using Moonlight.Features.FileManager.Services
|
||||
|
||||
@inject ToastService ToastService
|
||||
@inject AlertService AlertService
|
||||
@inject SharedFileAccessService SharedFileAccessService
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
@implements IDisposable
|
||||
|
||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||
<table class="w-100 table table-responsive table-row-bordered">
|
||||
|
@ -181,6 +186,9 @@
|
|||
<li>
|
||||
<a href="#" @onclick:preventDefault @onclick="() => Rename(entry)" class="dropdown-item">Rename</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" @onclick:preventDefault @onclick="() => Download(entry)" class="dropdown-item">Download</a>
|
||||
</li>
|
||||
@if (OnMoveRequested != null)
|
||||
{
|
||||
<li>
|
||||
|
@ -266,7 +274,9 @@
|
|||
await OnFileClicked.Invoke(fileEntry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Actions
|
||||
|
||||
private async Task Delete(params FileEntry[] entries)
|
||||
{
|
||||
if (entries.Length == 0)
|
||||
|
@ -294,8 +304,8 @@
|
|||
private async Task Rename(FileEntry fileEntry)
|
||||
{
|
||||
var name = await AlertService.Text($"Rename '{fileEntry.Name}'", "", fileEntry.Name);
|
||||
|
||||
if(string.IsNullOrEmpty(name))
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return;
|
||||
|
||||
await FileAccess.Move(fileEntry.Name, name);
|
||||
|
@ -305,12 +315,34 @@
|
|||
|
||||
private async Task RequestMove(FileEntry fileEntry)
|
||||
{
|
||||
if(OnMoveRequested == null)
|
||||
if (OnMoveRequested == null)
|
||||
return;
|
||||
|
||||
await OnMoveRequested.Invoke(fileEntry);
|
||||
}
|
||||
|
||||
private async Task Download(FileEntry fileEntry)
|
||||
{
|
||||
try
|
||||
{
|
||||
await SharedFileAccessService.Register(FileAccess);
|
||||
var token = await SharedFileAccessService.GenerateToken(FileAccess);
|
||||
var url = $"/api/download?token={token}&name={fileEntry.Name}";
|
||||
|
||||
await ToastService.Info("Starting download...");
|
||||
Navigation.NavigateTo(url, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn("Unable to start download");
|
||||
Logger.Warn(e);
|
||||
|
||||
await ToastService.Danger("Failed to start download");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Selection
|
||||
|
||||
private async Task HandleSelected(FileEntry fileEntry, ChangeEventArgs args)
|
||||
|
@ -334,7 +366,7 @@
|
|||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
|
||||
private async Task ToggleAll(ChangeEventArgs args)
|
||||
{
|
||||
if (args.Value == null)
|
||||
|
@ -366,7 +398,7 @@
|
|||
{
|
||||
await loader.SetText("Switching directory on target");
|
||||
await FileAccess.ChangeDirectory(name);
|
||||
|
||||
|
||||
if (OnPathChanged != null)
|
||||
await OnPathChanged.Invoke(await FileAccess.GetCurrentDirectory());
|
||||
});
|
||||
|
@ -378,7 +410,7 @@
|
|||
{
|
||||
await loader.SetText("Switching directory on target");
|
||||
await FileAccess.SetDirectory(path);
|
||||
|
||||
|
||||
if (OnPathChanged != null)
|
||||
await OnPathChanged.Invoke(await FileAccess.GetCurrentDirectory());
|
||||
});
|
||||
|
@ -387,4 +419,9 @@
|
|||
#endregion
|
||||
|
||||
public async Task Refresh() => await LazyLoader.Reload();
|
||||
|
||||
public async void Dispose()
|
||||
{
|
||||
await SharedFileAccessService.Unregister(FileAccess);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue