Implemented download in the file manager. Made file access jwt more modular

This commit is contained in:
Marcel Baumgartner 2024-02-06 22:23:47 +01:00
parent 26ed50c94b
commit 423616b9f3
5 changed files with 115 additions and 15 deletions

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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);
}
});

View file

@ -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);
}
}