Files
CollabVMAuthServer/CollabVMAuthServer/HTTP/AdminRoutes.cs
2025-01-19 16:15:24 -05:00

499 lines
18 KiB
C#

using System.Net;
using System.Text.Json;
using Computernewb.CollabVMAuthServer.HTTP.Payloads;
using Computernewb.CollabVMAuthServer.HTTP.Responses;
namespace Computernewb.CollabVMAuthServer.HTTP;
public static class AdminRoutes
{
public static void RegisterRoutes(IEndpointRouteBuilder app)
{
app.MapPost("/api/v1/admin/users", (Delegate)HandleAdminUsers);
app.MapPost("/api/v1/admin/updateuser", (Delegate)HandleAdminUpdateUser);
app.MapPost("/api/v1/admin/updatebot", (Delegate)HandleAdminUpdateBot);
app.MapPost("/api/v1/admin/ban", (Delegate)HandleBanUser);
app.MapPost("/api/v1/admin/ipban", (Delegate)HandleIPBan);
}
private static async Task<IResult> HandleIPBan(HttpContext context)
{
// Check payload
if (context.Request.ContentType != "application/json")
{
context.Response.StatusCode = 400;
return Results.Json(new IPBanResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
IPBanPayload? payload;
try
{
payload = await context.Request.ReadFromJsonAsync<IPBanPayload>();
}
catch (JsonException ex)
{
Utilities.Log(LogLevel.DEBUG, $"Failed to parse JSON: {ex.Message}");
context.Response.StatusCode = 400;
return Results.Json(new IPBanResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
if (payload == null || string.IsNullOrWhiteSpace(payload.session) || string.IsNullOrWhiteSpace(payload.ip) || (payload.banned && string.IsNullOrWhiteSpace(payload.reason)) || payload.banned == null || !IPAddress.TryParse(payload.ip, out var ip))
{
context.Response.StatusCode = 400;
return Results.Json(new IPBanResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
// Check token
var user = await Utilities.GetStaffByToken(payload.session);
if (user == null)
{
context.Response.StatusCode = 400;
return Results.Json(new IPBanResponse
{
success = false,
error = "Invalid session"
}, Utilities.JsonSerializerOptions);
}
// Check rank
if (user.Rank != Rank.Admin && user.Rank != Rank.Moderator)
{
context.Response.StatusCode = 403;
return Results.Json(new IPBanResponse
{
success = false,
error = "Insufficient permissions"
}, Utilities.JsonSerializerOptions);
}
// Set ban
if (payload.banned)
{
await Program.Database.BanIP(ip, payload.reason, user.Username);
}
else
{
await Program.Database.UnbanIP(ip);
}
return Results.Json(new IPBanResponse
{
success = true
}, Utilities.JsonSerializerOptions);
}
private static async Task<IResult> HandleBanUser(HttpContext context)
{
// Check payload
if (context.Request.ContentType != "application/json")
{
context.Response.StatusCode = 400;
return Results.Json(new BanUserResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
BanUserPayload? payload;
try
{
payload = await context.Request.ReadFromJsonAsync<BanUserPayload>();
}
catch (JsonException ex)
{
Utilities.Log(LogLevel.DEBUG, $"Failed to parse JSON: {ex.Message}");
context.Response.StatusCode = 400;
return Results.Json(new BanUserResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
if (payload == null || string.IsNullOrWhiteSpace(payload.token) || string.IsNullOrWhiteSpace(payload.username) || (payload.banned && string.IsNullOrWhiteSpace(payload.reason)) || payload.banned == null)
{
context.Response.StatusCode = 400;
return Results.Json(new BanUserResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
// Check token
var user = await Utilities.GetStaffByToken(payload.token);
if (user == null)
{
context.Response.StatusCode = 400;
return Results.Json(new IPBanResponse
{
success = false,
error = "Invalid session"
}, Utilities.JsonSerializerOptions);
}
// Check rank
if (user.Rank != Rank.Admin && user.Rank != Rank.Moderator)
{
context.Response.StatusCode = 403;
return Results.Json(new IPBanResponse
{
success = false,
error = "Insufficient permissions"
}, Utilities.JsonSerializerOptions);
}
// Check target user
var targetUser = await Program.Database.GetUser(payload.username);
if (targetUser == null)
{
context.Response.StatusCode = 400;
return Results.Json(new BanUserResponse
{
success = false,
error = "User not found"
}, Utilities.JsonSerializerOptions);
}
// Set ban
await Program.Database.SetBanned(targetUser.Username, payload.banned, payload.banned ? payload.reason : null);
return Results.Json(new BanUserResponse
{
success = true
}, Utilities.JsonSerializerOptions);
}
private static async Task<IResult> HandleAdminUpdateBot(HttpContext context)
{
// Check payload
if (context.Request.ContentType != "application/json")
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateBotResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
AdminUpdateBotPayload? payload;
try
{
payload = await context.Request.ReadFromJsonAsync<AdminUpdateBotPayload>();
}
catch (JsonException ex)
{
Utilities.Log(LogLevel.DEBUG, $"Failed to parse JSON: {ex.Message}");
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateBotResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
if (payload == null || string.IsNullOrWhiteSpace(payload.token) || string.IsNullOrWhiteSpace(payload.username))
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateBotResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
// Check token
var user = await Utilities.GetStaffByToken(payload.token);
if (user == null)
{
context.Response.StatusCode = 400;
return Results.Json(new IPBanResponse
{
success = false,
error = "Invalid session"
}, Utilities.JsonSerializerOptions);
}
// Check rank
if (user.Rank != Rank.Admin && user.Rank != Rank.Moderator)
{
context.Response.StatusCode = 403;
return Results.Json(new IPBanResponse
{
success = false,
error = "Insufficient permissions"
}, Utilities.JsonSerializerOptions);
}
// Check target bot
var targetBot = await Program.Database.GetBot(payload.username);
if (targetBot == null)
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateBotResponse
{
success = false,
error = "Bot not found"
}, Utilities.JsonSerializerOptions);
}
// Make sure at least one field is being updated
if (payload.rank == null)
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateBotResponse
{
success = false,
error = "No fields to update"
}, Utilities.JsonSerializerOptions);
}
// Moderators cannot promote bots to admin, and can only promote their own bots to moderator
else if ((Rank)payload.rank == Rank.Admin && user.Rank == Rank.Moderator)
{
context.Response.StatusCode = 403;
return Results.Json(new AdminUpdateBotResponse
{
success = false,
error = "Insufficient permissions"
}, Utilities.JsonSerializerOptions);
}
if (targetBot.Owner != user.Username && user.Rank == Rank.Moderator)
{
context.Response.StatusCode = 403;
return Results.Json(new AdminUpdateBotResponse
{
success = false,
error = "Insufficient permissions"
}, Utilities.JsonSerializerOptions);
}
// Check rank
int? rank = payload.rank;
if (rank != null && rank < 1 || rank > 3)
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateBotResponse
{
success = false,
error = "Invalid rank"
}, Utilities.JsonSerializerOptions);
}
// Update rank
await Program.Database.UpdateBot(targetBot.Username, newRank: payload.rank);
return Results.Json(new AdminUpdateBotResponse
{
success = true
}, Utilities.JsonSerializerOptions);
}
private static async Task<IResult> HandleAdminUpdateUser(HttpContext context)
{
// Check payload
if (context.Request.ContentType != "application/json")
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateUserResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
AdminUpdateUserPayload? payload;
try
{
payload = await context.Request.ReadFromJsonAsync<AdminUpdateUserPayload>();
}
catch (JsonException ex)
{
Utilities.Log(LogLevel.DEBUG, $"Failed to parse JSON: {ex.Message}");
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateUserResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
if (payload == null || string.IsNullOrWhiteSpace(payload.token) || string.IsNullOrWhiteSpace(payload.username))
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateUserResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
// Check token
var user = await Utilities.GetStaffByToken(payload.token);
if (user == null)
{
context.Response.StatusCode = 400;
return Results.Json(new IPBanResponse
{
success = false,
error = "Invalid session"
}, Utilities.JsonSerializerOptions);
}
// Check rank
if (user.Rank != Rank.Admin && user.Rank != Rank.Moderator)
{
context.Response.StatusCode = 403;
return Results.Json(new IPBanResponse
{
success = false,
error = "Insufficient permissions"
}, Utilities.JsonSerializerOptions);
}
// Check target user
var targetUser = await Program.Database.GetUser(payload.username);
if (targetUser == null)
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateUserResponse
{
success = false,
error = "User not found"
}, Utilities.JsonSerializerOptions);
}
// Check rank
int? rank = payload.rank;
if (rank != null && rank < 1 || rank > 3)
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUpdateUserResponse
{
success = false,
error = "Invalid rank"
}, Utilities.JsonSerializerOptions);
}
// Moderators cannot change ranks
if (user.Rank == Rank.Moderator && rank != null)
{
context.Response.StatusCode = 403;
return Results.Json(new AdminUpdateUserResponse
{
success = false,
error = "Insufficient permissions"
}, Utilities.JsonSerializerOptions);
}
// Check developer
bool? developer = payload.developer;
// Update rank
await Program.Database.UpdateUser(targetUser.Username, newRank: payload.rank, developer: developer);
if (developer == false)
{
await Program.Database.DeleteBots(targetUser.Username);
}
return Results.Json(new AdminUpdateUserResponse
{
success = true
}, Utilities.JsonSerializerOptions);
}
private static async Task<IResult> HandleAdminUsers(HttpContext context)
{
// Check payload
if (context.Request.ContentType != "application/json")
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUsersResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
AdminUsersPayload? payload;
try
{
payload = await context.Request.ReadFromJsonAsync<AdminUsersPayload>();
}
catch (JsonException ex)
{
Utilities.Log(LogLevel.DEBUG, $"Failed to parse JSON: {ex.Message}");
context.Response.StatusCode = 400;
return Results.Json(new AdminUsersResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
if (payload == null || string.IsNullOrWhiteSpace(payload.token) || payload.page < 1 || payload.resultsPerPage < 1)
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUsersResponse
{
success = false,
error = "Invalid request body"
}, Utilities.JsonSerializerOptions);
}
// Check token
var user = await Utilities.GetStaffByToken(payload.token);
if (user == null)
{
context.Response.StatusCode = 400;
return Results.Json(new IPBanResponse
{
success = false,
error = "Invalid session"
}, Utilities.JsonSerializerOptions);
}
// Check rank
if (user.Rank != Rank.Admin && user.Rank != Rank.Moderator)
{
context.Response.StatusCode = 403;
return Results.Json(new IPBanResponse
{
success = false,
error = "Insufficient permissions"
}, Utilities.JsonSerializerOptions);
}
// Validate orderBy
if (payload.orderBy != null && !new string[] { "id", "username", "email", "date_of_birth", "cvm_rank", "banned", "created" }.Contains(payload.orderBy))
{
context.Response.StatusCode = 400;
return Results.Json(new AdminUsersResponse
{
success = false,
error = "Invalid orderBy"
}, Utilities.JsonSerializerOptions);
}
// Get users
string? filterUsername = null;
if (payload.filterUsername != null)
{
filterUsername = "%" + payload.filterUsername
.Replace("%", "!%")
.Replace("!", "!!")
.Replace("_", "!_")
.Replace("[", "![") + "%";
}
IPAddress? filterIp = null;
if (payload.filterIp != null)
{
if (!IPAddress.TryParse(payload.filterIp, out var ip)) {
context.Response.StatusCode = 400;
return Results.Json(new AdminUsersResponse
{
success = false,
error = "Invalid filterIp"
}, Utilities.JsonSerializerOptions);
}
filterIp = IPAddress.Parse(payload.filterIp);
}
var users = (await Program.Database.ListUsers(filterUsername, filterIp, payload.orderBy ?? "id", payload.orderByDescending)).Select(user => new AdminUser
{
id = user.Id,
username = user.Username,
email = user.Email,
rank = (int)user.Rank,
banned = user.Banned,
banReason = user.BanReason ?? "",
dateOfBirth = user.DateOfBirth.ToString("yyyy-MM-dd"),
dateJoined = user.Joined.ToString("yyyy-MM-dd HH:mm:ss"),
registrationIp = user.RegistrationIP.ToString(),
developer = user.Developer
}).ToArray();
var page = users.Skip((payload.page - 1) * payload.resultsPerPage).Take(payload.resultsPerPage).ToArray();
return Results.Json(new AdminUsersResponse
{
success = true,
users = page,
totalPageCount = (int)Math.Ceiling(users.Length / (double)payload.resultsPerPage)
}, Utilities.JsonSerializerOptions);
}
}