type GroupPermissionsDef = Record interface GroupDef { size: number permissions: GroupPermissionsDef meta_permissions?: Record> } type PermissionDef = Record import raw from "./permissions.config.json" const config = raw as const const __validate_config = (config: unknown): config is PermissionDef => { const error = (message: string) => new Error("Failed to parse permissions.config.json: " + message) if (typeof config !== "object" || config === null) throw error("configuration is not an object or null.") for (const [group, definition] of Object.entries(config)) { if (typeof definition !== "object") throw error(`definition for ${group} is not an object (is ${typeof definition})`) if (definition === null) throw error(`definition for ${group} is null`) if (typeof definition.size !== "number") throw error(`type of size in group ${group} is not number (is ${typeof definition.size})`) if (typeof definition.permissions !== "object") throw error(`definition of permissions for group ${group} is not an object`) for (const [name, position] of Object.entries(definition.permissions)) { if (typeof position !== "number") throw error(`position of ${name} in group ${group} is not a number (is ${typeof position})`) if (position >= definition.size) throw error(`position ${position} of permission ${name} in group ${group} is out of bounds (size is ${definition.size})`) } if (definition.meta_permissions !== undefined) { const permissions = Object.keys(definition.permissions) if (typeof definition.meta_permissions !== "object") throw error(`meta_permissions of group ${group} is not an object (is ${typeof definition.meta_permissions})`) for (const [name, parts] of Object.entries(definition.meta_permissions)) { if (Object.keys(definition.permissions).includes(name)) throw error(`meta permission ${name} uses the same name as the permission`) if (typeof parts !== "object" && !(parts instanceof Array) || parts === null) throw error(`definition of meta permission ${name} in group ${group} is not an array`) for (const partial of (parts as Array)) { if (!permissions.includes(partial)) throw error(`permission ${partial} of definition of meta permission ${name} in group ${group} is not a permission of this group`) } } } } return true } type PermissionGroups = keyof typeof config function toPermissionObj(config: PermissionDef): Record> { let curr_pos = 0 const obj: Record> = {} for (const [name, definition] of Object.entries(config)) { obj[name] = {} obj[name]["ALL"] = 0; for (const [permission, value] of Object.entries(definition.permissions)) { if (permission == "ALL") { throw new Error(`permission must not be called ALL in group ${name}`) } obj[name][permission] = 1 << (curr_pos + value) obj[name]["ALL"] |= obj[name][permission] } for (const [meta, permissions] of Object.entries(definition?.meta_permissions ?? {})) { let mask = 0; for (const permission in permissions) { mask |= 1 << definition.permissions[permission] } obj[name][meta] = mask << curr_pos } curr_pos += definition.size } return obj } __validate_config(config) export default { ...toPermissionObj(config), has: (user_permissions: number, permissions: number): boolean => (user_permissions & permissions) == permissions, any: (user_permissions: number, permissions: number): boolean => (user_permissions & permissions) > 0, }