Update: Text Channel Stuff
Bugs: Files don't work Bugs: Video In-Line don't work Added: idk, everything?
This commit is contained in:
160
RelayServer/Services/Data/PermissionService.cs
Normal file
160
RelayServer/Services/Data/PermissionService.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using RelayServer.Models;
|
||||
using SurrealDb.Net;
|
||||
|
||||
namespace RelayServer.Services.Data;
|
||||
|
||||
public sealed class PermissionService
|
||||
{
|
||||
private readonly SurrealDbClient _db;
|
||||
|
||||
public PermissionService(SurrealDbClient db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task<bool> CanSendMessagesAsync(string username, string channelId)
|
||||
{
|
||||
if (await IsOwnerOrAdminAsync(username))
|
||||
return true;
|
||||
|
||||
if (await IsChannelReadOnlyAsync(channelId))
|
||||
return false;
|
||||
|
||||
return await HasPermissionAsync(username, channelId, PermissionFlags.SendMessages);
|
||||
}
|
||||
|
||||
public async Task<bool> CanManageChannelsAsync(string username) =>
|
||||
await IsOwnerOrAdminAsync(username) ||
|
||||
await HasGlobalPermissionAsync(username, PermissionFlags.ManageChannels);
|
||||
|
||||
public async Task<bool> CanManageMessagesAsync(string username, string channelId) =>
|
||||
await IsOwnerOrAdminAsync(username) ||
|
||||
await HasPermissionAsync(username, channelId, PermissionFlags.ManageMessages);
|
||||
|
||||
public async Task<bool> IsAdministratorAsync(string username) =>
|
||||
await IsOwnerOrAdminAsync(username);
|
||||
|
||||
public async Task<bool> CanViewChannelAsync(string username, string channelId)
|
||||
{
|
||||
if (await IsOwnerOrAdminAsync(username)) return true;
|
||||
return !await IsDeniedByChannelAsync(username, channelId, PermissionFlags.ViewChannel);
|
||||
}
|
||||
|
||||
public async Task<bool> CanSpeakAsync(string username, string channelId)
|
||||
{
|
||||
if (await IsOwnerOrAdminAsync(username)) return true;
|
||||
return !await IsDeniedByChannelAsync(username, channelId, PermissionFlags.Speak);
|
||||
}
|
||||
|
||||
public async Task<bool> CanDeleteChannelAsync(string username) =>
|
||||
await IsOwnerOrAdminAsync(username) ||
|
||||
await HasGlobalPermissionAsync(username, PermissionFlags.ManageChannels) ||
|
||||
await HasGlobalPermissionAsync(username, PermissionFlags.DeleteChannel);
|
||||
|
||||
public async Task<bool> CanEditChannelAsync(string username) =>
|
||||
await IsOwnerOrAdminAsync(username) ||
|
||||
await HasGlobalPermissionAsync(username, PermissionFlags.ManageChannels) ||
|
||||
await HasGlobalPermissionAsync(username, PermissionFlags.EditChannel);
|
||||
|
||||
private async Task<bool> IsOwnerOrAdminAsync(string username)
|
||||
{
|
||||
if (await IsServerOwnerAsync(username))
|
||||
return true;
|
||||
|
||||
var roles = await GetUserRolesAsync(username);
|
||||
return roles.Any(r => r.Permissions.HasFlag(PermissionFlags.Administrator));
|
||||
}
|
||||
|
||||
private async Task<bool> HasPermissionAsync(
|
||||
string username, string channelId, PermissionFlags flag)
|
||||
{
|
||||
if (await IsOwnerOrAdminAsync(username))
|
||||
return true;
|
||||
|
||||
var userRoles = await GetUserRolesAsync(username);
|
||||
if (userRoles.Count == 0) return false;
|
||||
|
||||
var channelOverrides = await GetChannelPermissionsAsync(channelId);
|
||||
var userRoleIds = new HashSet<string>(userRoles.Select(r => GetRecordIdString(r.Id)));
|
||||
|
||||
foreach (var co in channelOverrides.Where(co => userRoleIds.Contains(co.RoleId)))
|
||||
if (co.Deny.HasFlag(flag)) return false;
|
||||
|
||||
foreach (var co in channelOverrides.Where(co => userRoleIds.Contains(co.RoleId)))
|
||||
if (co.Allow.HasFlag(flag)) return true;
|
||||
|
||||
return userRoles.Any(r => r.Permissions.HasFlag(flag));
|
||||
}
|
||||
|
||||
private async Task<bool> HasGlobalPermissionAsync(string username, PermissionFlags flag)
|
||||
{
|
||||
var roles = await GetUserRolesAsync(username);
|
||||
return roles.Any(r =>
|
||||
r.Permissions.HasFlag(PermissionFlags.Administrator) ||
|
||||
r.Permissions.HasFlag(flag));
|
||||
}
|
||||
|
||||
private async Task<bool> IsDeniedByChannelAsync(string username, string channelId, PermissionFlags flag)
|
||||
{
|
||||
var userRoles = await GetUserRolesAsync(username);
|
||||
if (userRoles.Count == 0) return false;
|
||||
|
||||
var channelOverrides = await GetChannelPermissionsAsync(channelId);
|
||||
var userRoleIds = new HashSet<string>(userRoles.Select(r => GetRecordIdString(r.Id)));
|
||||
|
||||
return channelOverrides
|
||||
.Where(co => userRoleIds.Contains(co.RoleId))
|
||||
.Any(co => co.Deny.HasFlag(flag));
|
||||
}
|
||||
|
||||
private async Task<bool> IsServerOwnerAsync(string username)
|
||||
{
|
||||
var userId = $"users:{username.ToLower()}";
|
||||
var members = await _db.Select<ServerMembers>("server_members");
|
||||
return members.Any(m =>
|
||||
string.Equals(m.UserId, userId, StringComparison.OrdinalIgnoreCase) &&
|
||||
m.IsOwner);
|
||||
}
|
||||
|
||||
private async Task<List<Roles>> GetUserRolesAsync(string username)
|
||||
{
|
||||
var userId = $"users:{username.ToLower()}";
|
||||
|
||||
var userRoleLinks = await _db.Select<UserRoles>("user_roles");
|
||||
var userRoleIds = userRoleLinks
|
||||
.Where(ur => string.Equals(ur.UserId, userId, StringComparison.OrdinalIgnoreCase))
|
||||
.Select(ur => ur.RoleId)
|
||||
.ToHashSet();
|
||||
|
||||
if (userRoleIds.Count == 0) return [];
|
||||
|
||||
var allRoles = await _db.Select<Roles>("roles");
|
||||
return allRoles
|
||||
.Where(r => userRoleIds.Contains(GetRecordIdString(r.Id)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private async Task<List<ChannelPermissions>> GetChannelPermissionsAsync(string channelId)
|
||||
{
|
||||
var all = await _db.Select<ChannelPermissions>("channel_permissions");
|
||||
return all.Where(cp => cp.ChannelId == channelId).ToList();
|
||||
}
|
||||
|
||||
private async Task<bool> IsChannelReadOnlyAsync(string channelId)
|
||||
{
|
||||
var channels = await _db.Select<Channels>("channels");
|
||||
var channel = channels.FirstOrDefault(c => GetRecordIdString(c.Id) == channelId);
|
||||
return channel?.IsReadOnly ?? false;
|
||||
}
|
||||
|
||||
private static string GetRecordIdString(object? id)
|
||||
{
|
||||
if (id is null) return string.Empty;
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(id);
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
var recordId = root.GetProperty("Id").GetString() ?? string.Empty;
|
||||
var table = root.GetProperty("Table").GetString() ?? string.Empty;
|
||||
return $"{table}:{recordId}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user