From 849f2217339b33769a15a8fc6df0807e4ab99159 Mon Sep 17 00:00:00 2001 From: Rory& Date: Thu, 8 Oct 2026 12:33:25 +0200 Subject: [PATCH] Add instance bans to admin api user delete --- .../SpacebarAuthenticationConfiguration.cs | 2 +- .../Controllers/UserController.cs | 60 ++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/extra/admin-api/Interop/Spacebar.Interop.Authentication/SpacebarAuthenticationConfiguration.cs b/extra/admin-api/Interop/Spacebar.Interop.Authentication/SpacebarAuthenticationConfiguration.cs index 8fb81b282..cc6e4523f 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Authentication/SpacebarAuthenticationConfiguration.cs +++ b/extra/admin-api/Interop/Spacebar.Interop.Authentication/SpacebarAuthenticationConfiguration.cs @@ -14,5 +14,5 @@ public class SpacebarAuthenticationConfiguration { public string? OverrideDid { get; set; } public bool DisableAuthentication { get; set; } = false; public bool Enforce2FA { get; set; } = true; - public TimeSpan AuthCacheExpiry { get; set; } = TimeSpan.FromSeconds(30); + public TimeSpan AuthCacheExpiry { get; set; } = TimeSpan.FromSeconds(300); } \ No newline at end of file diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs index 51ec84e4f..d24ec7484 100644 --- a/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Linq.Expressions; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -183,8 +184,21 @@ public class UserController( .SingleAsync(x => x.Id == id); } + // TODO remove me and move into separate class + private static readonly long EPOCH = 1420070400000; + private static ThreadLocal INCREMENT = new ThreadLocal(() => 0); + + private static long GenerateSnowflake() { + long time = (DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - EPOCH) << 22; + long worker = (Thread.CurrentThread.ManagedThreadId % 31) << 17; + long process = (Process.GetCurrentProcess().Id % 31) << 12; + long increment = INCREMENT.Value++; + increment %= 4096; + return time | worker | process | increment; + } + [HttpGet("{id}/delete")] - public async IAsyncEnumerable DeleteUser(long id, [FromQuery] int messageDeleteChunkSize = 100) { + public async IAsyncEnumerable DeleteUser(long id, [FromQuery] int messageDeleteChunkSize = 100, [FromQuery] bool ban = false) { (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); var user = await db.Users.FindAsync(id); @@ -194,6 +208,29 @@ public class UserController( yield break; } + if (ban) { + var userBanId = GenerateSnowflake(); + db.InstanceBans.Add(new() { + Id = userBanId, + UserId = id, + CreatedAt = DateTime.Now, + Reason = " No reason specified" + }); + await db.SaveChangesAsync(); + + foreach (var session in await db.Sessions.Where(x => x.UserId == id && x.LastSeenIp != null).ToListAsync()) + if (session.LastSeenIp is not null) + db.InstanceBans.Add(new() { + Id = GenerateSnowflake(), + IpAddress = session.LastSeenIp, + CreatedAt = DateTime.Now, + IsFromOtherInstanceBan = true, + OriginInstanceBanId = userBanId, + Reason = " No reason specified" + }); + await db.SaveChangesAsync(); + } + user.Data = "{}"; user.Deleted = true; user.Disabled = true; @@ -233,6 +270,27 @@ public class UserController( await db.Database.ExecuteSqlRawAsync("REINDEX TABLE messages"); } } + + var mq = db.Members.Where(x => x.Id == id); + await using var ctx = sp.CreateAsyncScope(); + await using var db2 = ctx.ServiceProvider.GetRequiredService(); + while (await mq.AnyAsync()) { + var memberships = mq.Take(100).AsAsyncEnumerable(); + List guildIds = []; + await foreach (var membership in memberships) { + db2.Members.Remove(membership); + guildIds.Add(membership.GuildId.ToString()); + } + + await db2.SaveChangesAsync(); + + yield return new("MEMBERSHIP_DELETE", new { + guild_ids = guildIds + }); + } + + db.Users.Remove(user); + await db.SaveChangesAsync(); } // [HttpGet("{id}/Dms")]