2025-07-30 04:34:54 +02:00
|
|
|
|
2025-08-22 13:54:27 +02:00
|
|
|
interface GroupPermissionDetail {
|
|
|
|
|
position: number
|
|
|
|
|
name: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface GroupMetaPermissionDetail {
|
|
|
|
|
permissions: Array<string>
|
|
|
|
|
name: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type GroupPermissionsDef = Record<string, GroupPermissionDetail>
|
|
|
|
|
type GroupMetaPermissionsDef = Record<string, GroupMetaPermissionDetail>
|
2025-07-30 04:34:54 +02:00
|
|
|
|
|
|
|
|
interface GroupDef {
|
|
|
|
|
size: number
|
|
|
|
|
permissions: GroupPermissionsDef
|
2025-08-22 13:54:27 +02:00
|
|
|
meta_permissions?: GroupMetaPermissionsDef
|
2025-07-30 04:34:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type PermissionDef = Record<string, GroupDef>
|
|
|
|
|
|
|
|
|
|
|
2025-08-22 13:54:27 +02:00
|
|
|
const _display_names = new Map<number, string>()
|
2025-07-30 04:34:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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.")
|
2025-08-22 13:54:27 +02:00
|
|
|
|
2025-07-30 04:34:54 +02:00
|
|
|
for (const [group, definition] of Object.entries(config)) {
|
|
|
|
|
if (typeof definition !== "object")
|
|
|
|
|
throw error(`definition for ${group} is not an object (is ${typeof definition})`)
|
2025-08-22 13:54:27 +02:00
|
|
|
if (definition == null)
|
2025-07-30 04:34:54 +02:00
|
|
|
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`)
|
2025-08-22 13:54:27 +02:00
|
|
|
|
|
|
|
|
for (const [name, detail] of Object.entries(definition.permissions)) {
|
|
|
|
|
if (typeof detail !== "object" || detail == null)
|
|
|
|
|
throw error(`definition for permission ${name} has to be an object`)
|
|
|
|
|
|
|
|
|
|
if (typeof detail.position !== "number")
|
|
|
|
|
throw error(`position of ${name} in group ${group} is not a number (is ${typeof detail.position})`)
|
|
|
|
|
if (detail.position >= definition.size)
|
2025-07-30 04:34:54 +02:00
|
|
|
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`)
|
2025-08-22 13:54:27 +02:00
|
|
|
if (typeof parts !== "object" || parts == null)
|
|
|
|
|
throw error(`definition of meta permission ${name} in group ${group} is not an object`)
|
|
|
|
|
if (typeof parts.permissions !== "object" || !(parts.permissions instanceof Array))
|
|
|
|
|
throw error(`definition of meta permission ${name} has to include a key "permissions" with a value of type Array<string>`)
|
|
|
|
|
for (const partial of (parts.permissions as Array<any>)) {
|
2025-07-30 04:34:54 +02:00
|
|
|
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<PermissionGroups, Record<string, number>> {
|
|
|
|
|
let curr_pos = 0
|
|
|
|
|
const obj: Record<string, Record<string, number>> = {}
|
|
|
|
|
|
|
|
|
|
for (const [name, definition] of Object.entries(config)) {
|
|
|
|
|
obj[name] = {}
|
2025-08-20 21:17:17 +02:00
|
|
|
obj[name]["ALL"] = 0;
|
2025-08-22 13:54:27 +02:00
|
|
|
for (const [permission, detail] of Object.entries(definition.permissions)) {
|
2025-08-20 21:17:17 +02:00
|
|
|
if (permission == "ALL") {
|
|
|
|
|
throw new Error(`permission must not be called ALL in group ${name}`)
|
|
|
|
|
}
|
2025-08-22 13:54:27 +02:00
|
|
|
const mask = 1 << (curr_pos + detail.position)
|
|
|
|
|
obj[name][permission] = mask
|
|
|
|
|
obj[name]["ALL"] |= mask
|
|
|
|
|
_display_names.set(mask, detail.name)
|
2025-07-30 04:34:54 +02:00
|
|
|
}
|
2025-08-22 13:54:27 +02:00
|
|
|
for (const [meta, detail] of Object.entries(definition?.meta_permissions ?? {})) {
|
2025-07-30 04:34:54 +02:00
|
|
|
let mask = 0;
|
2025-08-22 13:54:27 +02:00
|
|
|
for (const permission of detail.permissions) {
|
|
|
|
|
mask |= 1 << definition.permissions[permission].position
|
2025-07-30 04:34:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
obj[name][meta] = mask << curr_pos
|
2025-08-22 13:54:27 +02:00
|
|
|
_display_names.set(mask, detail.name)
|
2025-07-30 04:34:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curr_pos += definition.size
|
|
|
|
|
}
|
|
|
|
|
return obj
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__validate_config(config)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
...toPermissionObj(config),
|
|
|
|
|
|
2025-08-20 21:17:17 +02:00
|
|
|
has: (user_permissions: number, permissions: number): boolean => (user_permissions & permissions) == permissions,
|
|
|
|
|
any: (user_permissions: number, permissions: number): boolean => (user_permissions & permissions) > 0,
|
2025-08-22 13:54:27 +02:00
|
|
|
|
|
|
|
|
display_name: (permission: number): string|undefined => {
|
|
|
|
|
const name = _display_names.get(permission)
|
|
|
|
|
if (name != undefined) return name
|
|
|
|
|
|
|
|
|
|
const names: Array<string> = []
|
|
|
|
|
let mask = 1
|
|
|
|
|
while (permission >= mask) {
|
|
|
|
|
const current_perm = permission & mask
|
|
|
|
|
const current_name = _display_names.get(current_perm)
|
|
|
|
|
if (current_name != undefined) names.push(current_name)
|
|
|
|
|
|
|
|
|
|
mask <<= 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return names.join(" | ")
|
|
|
|
|
}
|
2025-07-30 04:34:54 +02:00
|
|
|
}
|
2025-08-22 13:54:27 +02:00
|
|
|
|