Strongly type RoomPermission and GlobalPermission

`user-groups.ts` now tracks the list of possible values for
`RoomPermission` and `GlobalPermission` in a const.
This commit is contained in:
Guangcong Luo
2020-06-21 01:22:55 -07:00
parent f9e79220dd
commit ee6e43b552
5 changed files with 54 additions and 22 deletions

View File

@@ -115,10 +115,10 @@ export const commands: ChatCommands = {
return this.errorReply(`User '${name}' is already a ${nextGroupName} in this room.`);
}
if (!user.can('makeroom')) {
if (currentGroup.id && !user.can(`room${currentGroup.id || 'voice'}`, null, room)) {
if (currentGroup.id && !user.can(`room${currentGroup.id || 'voice'}` as 'roomvoice', null, room)) {
return this.errorReply(`/${cmd} - Access denied for promoting/demoting from ${currentGroupName}.`);
}
if (nextSymbol !== ' ' && !user.can(`room${nextGroup.id || 'voice'}`, null, room)) {
if (nextSymbol !== ' ' && !user.can(`room${nextGroup.id || 'voice'}` as 'roomvoice', null, room)) {
return this.errorReply(`/${cmd} - Access denied for promoting/demoting to ${nextGroupName}.`);
}
}

View File

@@ -23,6 +23,9 @@ To reload chat commands:
*/
type RoomPermission = import('./user-groups').RoomPermission;
type GlobalPermission = import('./user-groups').GlobalPermission;
export type PageHandler = (this: PageContext, query: string[], user: User, connection: Connection)
=> Promise<string | null | void> | string | null | void;
export interface PageTable {
@@ -48,7 +51,7 @@ export type SettingsHandler = (
connection: Connection
) => {
label: string,
permission: boolean | string,
permission: boolean | RoomPermission | GlobalPermission,
// button label, command | disabled
options: [string, string | true][],
};
@@ -215,8 +218,10 @@ export class PageContext extends MessageContext {
this.title = 'Page';
}
can(permission: RoomPermission | GlobalPermission, target?: User | null, room?: Room | null): boolean;
can(permission: GlobalPermission, target?: User | null): boolean;
can(permission: string, target: User | null = null, room: Room | null = null) {
if (!this.user.can(permission, target, room)) {
if (!this.user.can(permission as any, target, room)) {
this.send(`<h2>Permission denied.</h2>`);
return false;
}
@@ -742,8 +747,10 @@ export class CommandContext extends MessageContext {
statusfilter(status: string) {
return Chat.statusfilter(status, this.user);
}
can(permission: RoomPermission | GlobalPermission, target?: User | null, room?: Room | null): boolean;
can(permission: GlobalPermission, target?: User | null): boolean;
can(permission: string, target: User | null = null, room: Room | null = null) {
if (!this.user.can(permission, target, room)) {
if (!this.user.can(permission as any, target, room)) {
this.errorReply(this.cmdToken + this.fullCmd + " - Access denied.");
return false;
}

View File

@@ -6,17 +6,7 @@
*/
import * as defaults from '../config/config-example';
export interface GroupInfo {
symbol: GroupSymbol;
id: ID;
name: string;
rank: number;
inherit?: GroupSymbol;
jurisdiction?: string;
globalGroupInPersonalRoom?: GroupSymbol;
[k: string]: string | true | number | undefined;
}
type GroupInfo = import('./user-groups').GroupInfo;
export type ConfigType = typeof defaults & {
groups: {[symbol: string]: GroupInfo},
@@ -68,7 +58,7 @@ export function cacheGroupData(config: ConfigType) {
// preserving permissions specifically declared for the higher group.
for (const key in inheritGroup) {
if (key in groupData) continue;
groupData[key] = inheritGroup[key];
(groupData as any)[key] = (inheritGroup as any)[key];
}
}
delete groupData['inherit'];

View File

@@ -1,9 +1,39 @@
import {FS} from '../lib/fs';
type GroupInfo = import('./config-loader').GroupInfo;
export const PLAYER_SYMBOL: GroupSymbol = '\u2606';
export const HOST_SYMBOL: GroupSymbol = '\u2605';
export const ROOM_PERMISSIONS = [
'addhtml', 'announce', 'ban', 'broadcast', 'bypassafktimer', 'declare', 'editprivacy', 'editroom', 'exportinputlog', 'game', 'gamemanagement', 'gamemoderation', 'joinbattle', 'kick', 'minigame', 'modchat', 'modchatall', 'modlog', 'mute', 'nooverride', 'receiveauthmessages', 'roombot', 'roomdriver', 'roommod', 'roomowner', 'roomvoice', 'showmedia', 'timer', 'tournaments', 'warn',
] as const;
export const GLOBAL_PERMISSIONS = [
// administrative
'bypassall', 'console', 'disableladder', 'lockdown', 'potd', 'rawpacket',
// other
'alts', 'autotimer', 'bypassblocks', 'forcepromote', 'forcerename', 'forcewin', 'gdeclare', 'ignorelimits', 'ip', 'lock', 'makeroom', 'rangeban', 'promote',
] as const;
export type RoomPermission = typeof ROOM_PERMISSIONS[number];
export type GlobalPermission = typeof GLOBAL_PERMISSIONS[number];
export type GroupInfo = {
symbol: GroupSymbol,
id: ID,
name: string,
rank: number,
inherit?: GroupSymbol,
jurisdiction?: string,
globalonly?: boolean,
roomonly?: boolean,
battleonly?: boolean,
root?: boolean,
globalGroupInPersonalRoom?: GroupSymbol,
} & {
[P in RoomPermission | GlobalPermission]?: string | boolean;
};
/**
* Auth table - a Map for which users are in which groups.
*
@@ -49,7 +79,10 @@ export abstract class Auth extends Map<ID, GroupSymbol | ''> {
});
}
static hasPermission(
symbol: GroupSymbol, permission: string, targetSymbol?: GroupSymbol, targetingSelf?: boolean
symbol: GroupSymbol,
permission: GlobalPermission | RoomPermission | 'jurisdiction',
targetSymbol?: GroupSymbol,
targetingSelf?: boolean
): boolean {
const group = Auth.getGroup(symbol);
if (group['root']) {
@@ -80,7 +113,7 @@ export abstract class Auth extends Map<ID, GroupSymbol | ''> {
}
return false;
}
static listJurisdiction(symbol: GroupSymbol, permission: string) {
static listJurisdiction(symbol: GroupSymbol, permission: GlobalPermission | RoomPermission) {
const symbols = Object.keys(Config.groups) as GroupSymbol[];
return symbols.filter(targetSymbol => Auth.hasPermission(symbol, permission, targetSymbol));
}

View File

@@ -41,7 +41,7 @@ const PERMALOCK_CACHE_TIME = 30 * 24 * 60 * 60 * 1000; // 30 days
const DEFAULT_TRAINER_SPRITES = [1, 2, 101, 102, 169, 170, 265, 266];
import {FS} from '../lib/fs';
import {Auth, GlobalAuth, PLAYER_SYMBOL, HOST_SYMBOL} from './user-groups';
import {Auth, GlobalAuth, PLAYER_SYMBOL, HOST_SYMBOL, RoomPermission, GlobalPermission} from './user-groups';
const MINUTES = 60 * 1000;
const IDLE_TIMER = 60 * MINUTES;
@@ -520,6 +520,8 @@ export class User extends Chat.MessageContext {
const auth = (room && !this.can('makeroom') ? room.auth.get(this.id) : this.group);
return auth in Config.groups && Config.groups[auth].rank >= Config.groups[minAuth].rank;
}
can(permission: RoomPermission | GlobalPermission, target?: User | null, room?: Room | BasicChatRoom | null): boolean;
can(permission: GlobalPermission, target?: User | null): boolean;
can(permission: string, target: User | null = null, room: Room | BasicChatRoom | null = null): boolean {
if (this.hasSysopAccess()) return true;
@@ -537,7 +539,7 @@ export class User extends Chat.MessageContext {
if (replaceGroup) group = replaceGroup;
}
return Auth.hasPermission(group, permission, targetGroup, target === this);
return Auth.hasPermission(group, permission as any, targetGroup, target === this);
}
/**
* Special permission check for system operators