Experimental Backend Channel Perms Integration
This commit is contained in:
parent
f2e8987add
commit
03aea11985
@ -7,6 +7,7 @@ import { WS } from '../../types/ws';
|
||||
import Deps from '../../utils/deps';
|
||||
import { updateUser, validateUser } from '../modules/middleware';
|
||||
import { WebSocket } from '../../ws/websocket';
|
||||
import { WSGuard } from '../../ws/modules/ws-guard';
|
||||
|
||||
export const router = Router();
|
||||
|
||||
@ -14,6 +15,7 @@ const channels = Deps.get<Channels>(Channels);
|
||||
const messages = Deps.get<Messages>(Messages);
|
||||
const pings = Deps.get<Pings>(Pings);
|
||||
const ws = Deps.get<WebSocket>(WebSocket);
|
||||
const guard = Deps.get<WSGuard>(WSGuard);
|
||||
|
||||
router.get('/:channelId/messages', updateUser, validateUser, async (req, res) => {
|
||||
const channelId = req.params.channelId;
|
||||
|
@ -7,14 +7,13 @@ import Roles from '../../data/roles';
|
||||
import Users from '../../data/users';
|
||||
import Guilds from '../../data/guilds';
|
||||
import GuildMembers from '../../data/guild-members';
|
||||
import { Prohibited } from '../../types/prohibited';
|
||||
import { PermissionTypes } from '../../types/permission-types';
|
||||
|
||||
export class WSGuard {
|
||||
constructor(
|
||||
private channels = Deps.get<Channels>(Channels),
|
||||
private guilds = Deps.get<Guilds>(Guilds),
|
||||
private guildMembers = Deps.get<GuildMembers>(GuildMembers),
|
||||
private members = Deps.get<GuildMembers>(GuildMembers),
|
||||
private roles = Deps.get<Roles>(Roles),
|
||||
private users = Deps.get<Users>(Users),
|
||||
private ws = Deps.get<WebSocket>(WebSocket),
|
||||
@ -30,33 +29,50 @@ export class WSGuard {
|
||||
}
|
||||
|
||||
public async validateIsOwner(client: Socket, guildId: string) {
|
||||
const isOwner = await Guild.exists({
|
||||
_id: guildId,
|
||||
ownerId: this.userId(client)
|
||||
});
|
||||
const ownerId = this.userId(client);
|
||||
const isOwner = await Guild.exists({ _id: guildId, ownerId });
|
||||
if (!isOwner)
|
||||
throw new TypeError('Only the guild owner can do this');
|
||||
}
|
||||
|
||||
public async canAccessChannel(client: Socket, channelId?: string, withUse = false) {
|
||||
const channel = await this.channels.get(channelId);
|
||||
const perms = (!withUse)
|
||||
? PermissionTypes.Text.READ_MESSAGES
|
||||
: PermissionTypes.Text.READ_MESSAGES | PermissionTypes.Text.SEND_MESSAGES;
|
||||
await this.validateCan(client, channel.guildId, perms);
|
||||
public async validateCan(client: Socket, guildId: string, permission: PermissionTypes.PermissionString) {
|
||||
const can = await this.can(permission, guildId, this.userId(client));
|
||||
this.validate(can, permission);
|
||||
}
|
||||
|
||||
public async validateCan(client: Socket, guildId: string | undefined, permission: PermissionTypes.PermissionString) {
|
||||
const userId = this.userId(client);
|
||||
|
||||
const member = await this.guildMembers.getInGuild(guildId, userId);
|
||||
const guild = await this.guilds.get(guildId);
|
||||
|
||||
const can = await this.roles.hasPermission(guild, member, permission)
|
||||
|| guild.ownerId === userId;
|
||||
|
||||
public async validateCanInChannel(client: Socket, channelId: string, permission: PermissionTypes.PermissionString) {
|
||||
const can = await this.canInChannel(permission, channelId, this.userId(client));
|
||||
this.validate(can, permission);
|
||||
}
|
||||
}
|
||||
|
||||
private async can(permission: PermissionTypes.PermissionString, guildId: string, userId: string) {
|
||||
const guild = await this.guilds.get(guildId);
|
||||
const member = await this.members.getInGuild(guildId, userId);
|
||||
|
||||
return guild.ownerId === member.userId
|
||||
|| this.roles.hasPermission(guild, member, permission);
|
||||
}
|
||||
|
||||
private async canInChannel(permission: PermissionTypes.PermissionString, channelId: string, userId: string) {
|
||||
const channel = await this.channels.get(channelId);
|
||||
const member = await this.members.getInGuild(channel.guildId, userId);
|
||||
|
||||
const overrides = channel.overrides?.filter(o => member.roleIds.includes(o.roleId)) ?? [];
|
||||
const cumulativeAllowPerms = overrides.reduce((prev, curr) => prev | curr.allow, 0);
|
||||
const cumulativeDenyPerms = overrides.reduce((prev, curr) => prev | curr.deny, 0);
|
||||
|
||||
const has = (totalPerms: number, permission: number) =>
|
||||
Boolean(totalPerms & permission)
|
||||
|| Boolean(totalPerms & PermissionTypes.General.ADMINISTRATOR);
|
||||
|
||||
const permNumber = PermissionTypes.Text[permission];
|
||||
const canInheritantly = await this.can(permission, channel.guildId, userId);
|
||||
const isAllowedByOverride = has(cumulativeAllowPerms, permNumber);
|
||||
const isDeniedByOverride = has(cumulativeDenyPerms, permNumber);
|
||||
|
||||
return (canInheritantly && !isDeniedByOverride) || isAllowedByOverride;
|
||||
}
|
||||
|
||||
// FIXME: you cannot combine string permissions with bitfields
|
||||
private validate(can: boolean, permission: PermissionTypes.PermissionString) {
|
||||
if (!can)
|
||||
|
@ -30,7 +30,7 @@ export class WSRooms {
|
||||
|
||||
for (const { id } of channels)
|
||||
try {
|
||||
await this.guard.canAccessChannel(client, id);
|
||||
await this.guard.validateCanInChannel(client, id, 'READ_MESSAGES');
|
||||
ids.push(id);
|
||||
} catch {}
|
||||
return ids;
|
||||
|
@ -21,7 +21,7 @@ export default class implements WSEvent<'MESSAGE_CREATE'> {
|
||||
const authorId = ws.sessions.userId(client);
|
||||
|
||||
const [_, message, author] = await Promise.all([
|
||||
this.guard.canAccessChannel(client, channelId, true),
|
||||
this.guard.validateCanInChannel(client, channelId, 'SEND_MESSAGES'),
|
||||
this.messages.create(authorId, channelId, { content, embed }),
|
||||
this.users.getSelf(authorId),
|
||||
]);
|
||||
|
@ -25,7 +25,7 @@ export default class implements WSEvent<'MESSAGE_DELETE'> {
|
||||
try {
|
||||
this.guard.validateIsUser(client, message.authorId);
|
||||
} catch {
|
||||
await this.guard.validateCan(client, channel.guildId, 'MANAGE_MESSAGES');
|
||||
await this.guard.validateCanInChannel(client, channel.id, 'MANAGE_MESSAGES');
|
||||
}
|
||||
await message.deleteOne();
|
||||
|
||||
|
@ -68,7 +68,7 @@ export class PermService {
|
||||
|
||||
public canMember(permission: PermissionTypes.PermissionString, guild: Entity.Guild, member: Entity.GuildMember) {
|
||||
return guild.ownerId === member.userId
|
||||
|| this.hasPermission(
|
||||
|| this.hasPerm(
|
||||
this.getTotalPerms(member, guild.id),
|
||||
PermissionTypes.All[permission] as number,
|
||||
);
|
||||
@ -85,8 +85,8 @@ export class PermService {
|
||||
|
||||
const permNumber = PermissionTypes.Text[permission];
|
||||
const canInheritantly = this.can(permission, guildId);
|
||||
const isAllowedByOverride = this.hasPermission(cumulativeAllowPerms, permNumber);
|
||||
const isDeniedByOverride = this.hasPermission(cumulativeDenyPerms, permNumber);
|
||||
const isAllowedByOverride = this.hasPerm(cumulativeAllowPerms, permNumber);
|
||||
const isDeniedByOverride = this.hasPerm(cumulativeDenyPerms, permNumber);
|
||||
|
||||
return (canInheritantly && !isDeniedByOverride) || isAllowedByOverride;
|
||||
}
|
||||
@ -96,17 +96,17 @@ export class PermService {
|
||||
const member = this.getSelfMember(guildId);
|
||||
|
||||
return guild.ownerId === member.userId
|
||||
|| this.hasPermission(
|
||||
|| this.hasPerm(
|
||||
this.getTotalPerms(member, guildId),
|
||||
PermissionTypes.All[permission] as number,
|
||||
);
|
||||
}
|
||||
public getTotalPerms(member: Entity.GuildMember, guildId: string) {
|
||||
private getTotalPerms(member: Entity.GuildMember, guildId: string) {
|
||||
return getGuildRoles(guildId)(this.state)
|
||||
.filter(r => member?.roleIds.includes(r.id))
|
||||
.reduce((acc, value) => value.permissions | acc, 0);
|
||||
}
|
||||
public hasPermission(totalPerms: number, permission: number) {
|
||||
private hasPerm(totalPerms: number, permission: number) {
|
||||
return Boolean(totalPerms & permission)
|
||||
|| Boolean(totalPerms & PermissionTypes.General.ADMINISTRATOR);
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
// using explicit properties in websocket args makes this redundant?
|
||||
/** Keys of objects that cannot be updated. */
|
||||
export namespace Prohibited {
|
||||
export const general: any = ['id', 'createdAt'];
|
||||
|
||||
export const app: (keyof Entity.App)[] = [
|
||||
...general,
|
||||
'owner',
|
||||
'user',
|
||||
];
|
||||
export const channel: (keyof Entity.Channel)[] = [
|
||||
...general,
|
||||
'guildId',
|
||||
'lastMessageId',
|
||||
'type',
|
||||
];
|
||||
export const guild: (keyof Entity.Guild)[] = [
|
||||
...general,
|
||||
'nameAcronym',
|
||||
];
|
||||
export const guildMember: (keyof Entity.GuildMember)[] = [
|
||||
...general,
|
||||
'guildId',
|
||||
'userId',
|
||||
];
|
||||
export const message: (keyof Entity.Message)[] = [
|
||||
...general,
|
||||
'authorId',
|
||||
'channelId',
|
||||
'updatedAt',
|
||||
];
|
||||
export const role: (keyof Entity.Role)[] = [
|
||||
...general,
|
||||
'guildId',
|
||||
];
|
||||
export const user: (keyof UserTypes.Self)[] = [
|
||||
...general,
|
||||
'channelIds',
|
||||
'guildIds',
|
||||
'badges',
|
||||
'bot',
|
||||
'verified',
|
||||
];
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user