diff --git a/extra/admin-api/Models/Spacebar.Models.Generic/Constants/Permissions.cs b/extra/admin-api/Models/Spacebar.Models.Generic/Constants/Permissions.cs
new file mode 100644
index 00000000..03b4112a
--- /dev/null
+++ b/extra/admin-api/Models/Spacebar.Models.Generic/Constants/Permissions.cs
@@ -0,0 +1,60 @@
+namespace Spacebar.Models.Generic.Constants;
+
+[Flags]
+public enum Permissions : ulong {
+ CreateInstantInvite = 1UL << 0,
+ KickMembers = 1UL << 1,
+ BanMembers = 1UL << 2,
+ Administrator = 1UL << 3,
+ ManageChannels = 1UL << 4,
+ ManageGuild = 1UL << 5,
+ AddReactions = 1UL << 6,
+ ViewAuditLog = 1UL << 7,
+ PrioritySpeaker = 1UL << 8,
+ Stream = 1UL << 9,
+ ViewChannel = 1UL << 10,
+ SendMessages = 1UL << 11,
+ SendTtsMessages = 1UL << 12,
+ ManageMessages = 1UL << 13,
+ EmbedLinks = 1UL << 14,
+ AttachFiles = 1UL << 15,
+ ReadMessageHistory = 1UL << 16,
+ MentionEveryone = 1UL << 17,
+ UseExternalEmojis = 1UL << 18,
+ ViewGuildInsights = 1UL << 19,
+ Connect = 1UL << 20,
+ Speak = 1UL << 21,
+ MuteMembers = 1UL << 22,
+ DeafenMembers = 1UL << 23,
+ MoveMembers = 1UL << 24,
+ UseVad = 1UL << 25,
+ ChangeNickname = 1UL << 26,
+ ManageNicknames = 1UL << 27,
+ ManageRoles = 1UL << 28,
+ ManageWebhooks = 1UL << 29,
+ ManageExpressions = 1UL << 30,
+ UseApplicationCommands = 1UL << 31,
+ RequestToSpeak = 1UL << 32,
+ ManageEvents = 1UL << 33,
+ ManageThreads = 1UL << 34,
+ CreatePublicThreads = 1UL << 35,
+ CreatePrivateThreads = 1UL << 36,
+ UseExternalStickers = 1UL << 37,
+ SendMessagesInThreads = 1UL << 38,
+ UseEmbeddedActivities = 1UL << 39,
+ ModerateMembers = 1UL << 40,
+ ViewCreatorMonetizationAnalytics = 1UL << 41,
+ UseSoundboard = 1UL << 42,
+ CreateExpressions = 1UL << 43,
+ CreateEvents = 1UL << 44,
+ UseExternalSounds = 1UL << 45,
+ SendVoiceMessages = 1UL << 46,
+
+ [Obsolete("Clyde is no longer available")]
+ UseClydeAi = 1UL << 47,
+ SetVoiceChannelStatus = 1UL << 48,
+ SendPolls = 1UL << 49,
+ UseExternalApps = 1UL << 50,
+ PinMessages = 1UL << 51,
+ BypassSlowmode = 1UL << 52
+}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.UApi/Program.cs b/extra/admin-api/Spacebar.UApi/Program.cs
index f6ce9829..afdb42ae 100644
--- a/extra/admin-api/Spacebar.UApi/Program.cs
+++ b/extra/admin-api/Spacebar.UApi/Program.cs
@@ -8,6 +8,7 @@ using Spacebar.ConfigModel.Extensions;
using Spacebar.Interop.Authentication;
using Spacebar.Interop.Authentication.AspNetCore;
using Spacebar.Models.Db.Contexts;
+using Spacebar.Models.Generic.Constants;
using Spacebar.UApi.Services;
var builder = WebApplication.CreateBuilder(args);
@@ -59,6 +60,7 @@ builder.Services.AddSingleton<SpacebarAuthenticationConfiguration>();
builder.Services.AddScoped<SpacebarAuthenticationService>();
builder.Services.AddScoped<SpacebarAspNetAuthenticationService>();
builder.Services.AddScoped<TemplateImportService>();
+builder.Services.AddScoped<PermissionService>();
var app = builder.Build();
diff --git a/extra/admin-api/Spacebar.UApi/Services/PermissionService.cs b/extra/admin-api/Spacebar.UApi/Services/PermissionService.cs
new file mode 100644
index 00000000..033dbb20
--- /dev/null
+++ b/extra/admin-api/Spacebar.UApi/Services/PermissionService.cs
@@ -0,0 +1,41 @@
+using Microsoft.EntityFrameworkCore;
+using Spacebar.Models.Db.Contexts;
+using Spacebar.Models.Generic.Constants;
+
+namespace Spacebar.UApi.Services;
+
+public class PermissionService(SpacebarDbContext db) {
+ /// <summary>
+ /// Asserts that user has all the relevant guild permissions
+ /// </summary>
+ /// <param name="permission">Permissions to require</param>
+ /// <param name="guildId">Guild ID</param>
+ /// <param name="userId">Member ID</param>
+ /// <exception cref="InvalidOperationException">Has one or more missing permissions</exception>
+ public async Task AssertUserHasGuildPermission(Permissions permission, string guildId, string userId) {
+ var member = await db.Members
+ .Include(x => x.Roles)
+ .SingleAsync(x => x.Id == userId && x.GuildId == guildId);
+
+ if (member is null)
+ throw new InvalidOperationException("You are not a member of this guild.");
+
+ var permissions = member.Roles.Aggregate((Permissions)0UL, (current, role) => current | (Permissions)ulong.Parse(role.Permissions));
+
+ if (member.CommunicationDisabledUntil is not null && member.CommunicationDisabledUntil > DateTime.UtcNow) {
+ permissions &= Permissions.ViewChannel | Permissions.ReadMessageHistory;
+ }
+
+ if (!permissions.HasFlag(permission))
+ throw new PermissionException(Enum.GetValues<Permissions>().Where(p => !permissions.HasFlag(p) && permission.HasFlag(p)));
+ }
+}
+
+public class PermissionException : Exception {
+ public IEnumerable<Permissions> MissingPermissions { get; }
+
+ public PermissionException(IEnumerable<Permissions> missingPermissions) : base(
+ $"You do not have the required permissions to perform this action: {string.Join(", ", missingPermissions)}") {
+ MissingPermissions = missingPermissions;
+ }
+}
\ No newline at end of file
|