Implemented user deletion service and IUserDeleteHandler for plugins to hook into

This commit is contained in:
2025-08-19 21:35:43 +02:00
parent 8a63a3448a
commit 60178dc54b
5 changed files with 96 additions and 2 deletions

View File

@@ -2,11 +2,13 @@ using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using MoonCore.Exceptions;
using MoonCore.Extended.Abstractions;
using MoonCore.Extended.Helpers;
using MoonCore.Models;
using Moonlight.ApiServer.Database.Entities;
using Moonlight.ApiServer.Services;
using Moonlight.Shared.Http.Requests.Admin.Users;
using Moonlight.Shared.Http.Responses.Admin.Users;
@@ -166,7 +168,7 @@ public class UsersController : Controller
[HttpDelete("{id}")]
[Authorize(Policy = "permissions:admin.users.delete")]
public async Task Delete([FromRoute] int id)
public async Task Delete([FromRoute] int id, [FromQuery] bool force = false)
{
var user = await UserRepository
.Get()
@@ -175,6 +177,16 @@ public class UsersController : Controller
if (user == null)
throw new HttpApiException("No user with that id found", 404);
await UserRepository.Remove(user);
var deletionService = HttpContext.RequestServices.GetRequiredService<UserDeletionService>();
if (!force)
{
var validationResult = await deletionService.Validate(user);
if (!validationResult.IsAllowed)
throw new HttpApiException($"Unable to delete user", 400, validationResult.Reason);
}
await deletionService.Delete(user, force);
}
}

View File

@@ -0,0 +1,10 @@
using Moonlight.ApiServer.Database.Entities;
using Moonlight.ApiServer.Models;
namespace Moonlight.ApiServer.Interfaces;
public interface IUserDeleteHandler
{
public Task<UserDeleteValidationResult> Validate(User user);
public Task Delete(User user, bool force);
}

View File

@@ -0,0 +1,27 @@
namespace Moonlight.ApiServer.Models;
public class UserDeleteValidationResult
{
public bool IsAllowed { get; set; }
public string Reason { get; set; }
public static UserDeleteValidationResult Allow()
{
return new UserDeleteValidationResult()
{
IsAllowed = true
};
}
public static UserDeleteValidationResult Deny()
=> Deny("No reason provided");
public static UserDeleteValidationResult Deny(string reason)
{
return new UserDeleteValidationResult()
{
IsAllowed = false,
Reason = reason
};
}
}

View File

@@ -0,0 +1,42 @@
using MoonCore.Extended.Abstractions;
using Moonlight.ApiServer.Database.Entities;
using Moonlight.ApiServer.Interfaces;
using Moonlight.ApiServer.Models;
namespace Moonlight.ApiServer.Services;
public class UserDeletionService
{
private readonly IUserDeleteHandler[] Handlers;
private readonly DatabaseRepository<User> UserRepository;
public UserDeletionService(
IEnumerable<IUserDeleteHandler> handlers,
DatabaseRepository<User> userRepository
)
{
UserRepository = userRepository;
Handlers = handlers.ToArray();
}
public async Task<UserDeleteValidationResult> Validate(User user)
{
foreach (var handler in Handlers)
{
var result = await handler.Validate(user);
if (!result.IsAllowed)
return result;
}
return UserDeleteValidationResult.Allow();
}
public async Task Delete(User user, bool force)
{
foreach (var handler in Handlers)
await Delete(user, force);
await UserRepository.Remove(user);
}
}

View File

@@ -6,6 +6,7 @@ using MoonCore.Extended.JwtInvalidation;
using MoonCore.Permissions;
using Moonlight.ApiServer.Implementations;
using Moonlight.ApiServer.Interfaces;
using Moonlight.ApiServer.Services;
namespace Moonlight.ApiServer.Startup;
@@ -47,6 +48,8 @@ public partial class Startup
if (Configuration.Authentication.EnableLocalOAuth2)
WebApplicationBuilder.Services.AddScoped<IOAuth2Provider, LocalOAuth2Provider>();
WebApplicationBuilder.Services.AddScoped<UserDeletionService>();
return Task.CompletedTask;
}