diff --git a/exp_groups/index.ts b/exp_groups/index.ts index 743f33b9..f0f8e5ce 100644 --- a/exp_groups/index.ts +++ b/exp_groups/index.ts @@ -12,123 +12,6 @@ declare module "@clusterio/lib" { } } -// Group permissions - -lib.definePermission({ - name: "exp_groups.group.get", - title: "Get Groups", - description: "Retrieve a specific Factorio permission group by ID.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.group.list", - title: "List Groups", - description: "List all Factorio permission groups.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.group.subscribe", - title: "Subscribe to Group Updates", - description: "Receive updates when Factorio permission groups change.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.group.create", - title: "Create Groups", - description: "Create new Factorio permission groups.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.group.update", - title: "Update Groups", - description: "Modify existing Factorio permission groups.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.group.delete", - title: "Delete Groups", - description: "Delete Factorio permission groups.", - grantByDefault: false, -}); - -// Assignment permissions - -lib.definePermission({ - name: "exp_groups.assignment.get", - title: "Get Assignments", - description: "Retrieve a specific manual group assignment for a player.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.assignment.list", - title: "List Assignments", - description: "List all manual group assignments.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.assignment.subscribe", - title: "Subscribe to Assignment Updates", - description: "Receive updates when manual group assignments change.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.assignment.create", - title: "Create Assignments", - description: "Manually assign players to groups, overriding role mappings.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.assignment.update", - title: "Update Assignments", - description: "Modify existing manual group assignments.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.assignment.delete", - title: "Delete Assignments", - description: "Remove manual group assignments.", - grantByDefault: false, -}); - -// Role mapping permissions - -lib.definePermission({ - name: "exp_groups.role_mapping.get", - title: "Get Role Mappings", - description: "Retrieve a specific role mapping rule.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.role_mapping.list", - title: "List Role Mappings", - description: "List all role mapping rules.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.role_mapping.subscribe", - title: "Subscribe to Role Mapping Updates", - description: "Receive updates when role mapping rules change.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.role_mapping.create", - title: "Create Role Mappings", - description: "Create rules that map user roles to Factorio permission groups.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.role_mapping.update", - title: "Update Role Mappings", - description: "Modify existing role mapping rules.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.role_mapping.delete", - title: "Delete Role Mappings", - description: "Delete role mapping rules.", - grantByDefault: false, -}); - export const plugin: lib.PluginDeclaration = { name: "exp_groups", title: "ExpGaming - Permission Groups", @@ -164,6 +47,125 @@ export const plugin: lib.PluginDeclaration = { messages.RoleMappingListRequest, ], + permissions: [ + // Group permissions + + { + name: "exp_groups.group.get", + title: "Get Groups", + description: "Retrieve a specific Factorio permission group by ID.", + grantByDefault: true, + }, + { + name: "exp_groups.group.list", + title: "List Groups", + description: "List all Factorio permission groups.", + grantByDefault: true, + }, + { + name: "exp_groups.group.subscribe", + title: "Subscribe to Group Updates", + description: "Receive updates when Factorio permission groups change.", + grantByDefault: true, + }, + { + name: "exp_groups.group.create", + title: "Create Groups", + description: "Create new Factorio permission groups.", + grantByDefault: false, + }, + { + name: "exp_groups.group.update", + title: "Update Groups", + description: "Modify existing Factorio permission groups.", + grantByDefault: false, + }, + { + name: "exp_groups.group.delete", + title: "Delete Groups", + description: "Delete Factorio permission groups.", + grantByDefault: false, + }, + + // Assignment permissions + + { + name: "exp_groups.assignment.get", + title: "Get Assignments", + description: "Retrieve a specific manual group assignment for a player.", + grantByDefault: true, + }, + { + name: "exp_groups.assignment.list", + title: "List Assignments", + description: "List all manual group assignments.", + grantByDefault: true, + }, + { + name: "exp_groups.assignment.subscribe", + title: "Subscribe to Assignment Updates", + description: "Receive updates when manual group assignments change.", + grantByDefault: true, + }, + { + name: "exp_groups.assignment.create", + title: "Create Assignments", + description: "Manually assign players to groups, overriding role mappings.", + grantByDefault: false, + }, + { + name: "exp_groups.assignment.update", + title: "Update Assignments", + description: "Modify existing manual group assignments.", + grantByDefault: false, + }, + { + name: "exp_groups.assignment.delete", + title: "Delete Assignments", + description: "Remove manual group assignments.", + grantByDefault: false, + }, + + // Role mapping permissions + + { + name: "exp_groups.role_mapping.get", + title: "Get Role Mappings", + description: "Retrieve a specific role mapping rule.", + grantByDefault: true, + }, + { + name: "exp_groups.role_mapping.list", + title: "List Role Mappings", + description: "List all role mapping rules.", + grantByDefault: true, + }, + { + name: "exp_groups.role_mapping.subscribe", + title: "Subscribe to Role Mapping Updates", + description: "Receive updates when role mapping rules change.", + grantByDefault: true, + }, + { + name: "exp_groups.role_mapping.create", + title: "Create Role Mappings", + description: "Create rules that map user roles to Factorio permission groups.", + grantByDefault: false, + }, + { + name: "exp_groups.role_mapping.update", + title: "Update Role Mappings", + description: "Modify existing role mapping rules.", + grantByDefault: false, + }, + { + name: "exp_groups.role_mapping.delete", + title: "Delete Role Mappings", + description: "Delete role mapping rules.", + grantByDefault: false, + }, + ], + instanceEntrypoint: "./dist/node/instance", instanceConfigFields: { "exp_groups.sync_mode": { diff --git a/exp_groups/messages.ts b/exp_groups/messages.ts index b0e5e986..d5191ca9 100644 --- a/exp_groups/messages.ts +++ b/exp_groups/messages.ts @@ -1,6 +1,30 @@ import * as lib from "@clusterio/lib"; import { Type, Static } from "@sinclair/typebox"; +// Declared here rather than in index.ts because the browser project cannot include index.ts, which re-exports controller.ts +declare module "@clusterio/lib" { + export interface Permissions { + "exp_groups.group.get": never; + "exp_groups.group.list": never; + "exp_groups.group.subscribe": never; + "exp_groups.group.create": never; + "exp_groups.group.update": never; + "exp_groups.group.delete": never; + "exp_groups.assignment.get": never; + "exp_groups.assignment.list": never; + "exp_groups.assignment.subscribe": never; + "exp_groups.assignment.create": never; + "exp_groups.assignment.update": never; + "exp_groups.assignment.delete": never; + "exp_groups.role_mapping.get": never; + "exp_groups.role_mapping.list": never; + "exp_groups.role_mapping.subscribe": never; + "exp_groups.role_mapping.create": never; + "exp_groups.role_mapping.update": never; + "exp_groups.role_mapping.delete": never; + } +} + /* Data records */ diff --git a/exp_scenario/index.ts b/exp_scenario/index.ts index f09c3c6f..5d04b902 100644 --- a/exp_scenario/index.ts +++ b/exp_scenario/index.ts @@ -1,29 +1,14 @@ import * as lib from "@clusterio/lib"; import * as messages from "./messages"; - -// Defines a permission for every in game action and role flag used by the scenario -import "./permissions"; - -lib.definePermission({ - name: "exp_scenario.config.view", - title: "View ExpScenario Config", - description: "View the config for all submodules of ExpScenario", -}); - -lib.definePermission({ - name: "exp_scenario.config.edit", - title: "Edit ExpScenario Config", - description: "Edit the config for all submodules of ExpScenario", -}); - -lib.definePermission({ - name: "exp_scenario.seed", - title: "Seed ExpScenario roles and groups", - description: "Create the roles and permission groups the scenario shipped with", -}); +import { permissions as scenarioPermissions, type ScenarioPermissionName } from "./permissions"; declare module "@clusterio/lib" { - + // Everything checked in game through exp_roles, plus the permissions checked on the controller + export interface Permissions extends Record { + "exp_scenario.config.view": never; + "exp_scenario.config.edit": never; + "exp_scenario.seed": never; + } } export const plugin: lib.PluginDeclaration = { @@ -37,5 +22,24 @@ export const plugin: lib.PluginDeclaration = { messages.SeedRequest, ], + permissions: [ + ...scenarioPermissions, + { + name: "exp_scenario.config.view", + title: "View ExpScenario Config", + description: "View the config for all submodules of ExpScenario", + }, + { + name: "exp_scenario.config.edit", + title: "Edit ExpScenario Config", + description: "Edit the config for all submodules of ExpScenario", + }, + { + name: "exp_scenario.seed", + title: "Seed ExpScenario roles and groups", + description: "Create the roles and permission groups the scenario shipped with", + }, + ], + webEntrypoint: "./web", }; diff --git a/exp_scenario/permissions.ts b/exp_scenario/permissions.ts index bbaafacd..5fc7aa52 100644 --- a/exp_scenario/permissions.ts +++ b/exp_scenario/permissions.ts @@ -1,4 +1,4 @@ -import * as lib from "@clusterio/lib"; +import type * as lib from "@clusterio/lib"; /** * Permissions checked by the scenario in game through exp_roles. @@ -7,9 +7,9 @@ import * as lib from "@clusterio/lib"; * underscores, see module/commands/_authorities.lua. Everything else is checked * by name at its call site. */ -type Definition = [name: string, title: string, description: string, grantByDefault?: boolean]; +type Definition = readonly [name: Name, title: string, description: string, grantByDefault?: boolean]; -const definitions: Definition[] = [ +const definitions = [ ["exp_scenario.bypass.deconstruction_log", "Deconstruction log bypass", "Be excluded from the deconstruction log."], ["exp_scenario.bypass.entity_protection", "Bypass entity protection", "Remove entities that the protection filter would block."], ["exp_scenario.bypass.nuke_protection", "Bypass nuke protection", "Use nukes without the nuke protection restrictions."], @@ -118,8 +118,12 @@ const definitions: Definition[] = [ ["exp_scenario.player.admin", "Factorio admin", "Be promoted to Factorio admin while holding a role with this permission."], ["exp_scenario.player.instant_respawn", "Instant respawn", "Respawn after two seconds instead of the default delay."], ["exp_scenario.player.spectator", "Spectator", "Remove the zoom to world noise effect, as Factorio does for spectators."], -]; +] as const satisfies readonly Definition[]; -for (const [name, title, description, grantByDefault] of definitions) { - lib.definePermission({ name, title, description, grantByDefault }); -} +/** Name of a permission defined by the scenario, added to lib.Permissions in index.ts. */ +export type ScenarioPermissionName = (typeof definitions)[number][0]; + +export const permissions = definitions.map((definition: Definition): lib.PermissionDefinition => { + const [name, title, description, grantByDefault] = definition; + return { name, title, description, grantByDefault }; +}); diff --git a/exp_scenario/seed.ts b/exp_scenario/seed.ts index 37edbd63..64b34550 100644 --- a/exp_scenario/seed.ts +++ b/exp_scenario/seed.ts @@ -1,3 +1,4 @@ +import type * as lib from "@clusterio/lib"; import { RoleColor } from "@expcluster/roles"; /** @@ -19,7 +20,7 @@ export interface SeedRole { isAdmin?: boolean; /** Name of the role whose permissions are also granted, applied recursively. */ parent?: string; - permissions: string[]; + permissions: lib.PermissionName[]; /** * Name of the seed group holders are placed in by their highest role. * Without one the holders stay in Factorio's Default group. @@ -329,7 +330,7 @@ export const seedGroups: SeedGroup[] = [ /** The permissions a seed role grants, including those of its parents. */ export function flattenSeedPermissions(role: SeedRole, roles = seedRoles) { - const permissions = new Set(); + const permissions = new Set(); const seen = new Set(); let current: SeedRole | undefined = role; while (current && !seen.has(current.name)) { diff --git a/exp_scenario/test/controller.test.js b/exp_scenario/test/controller.test.js index 18ffedc4..16206b95 100644 --- a/exp_scenario/test/controller.test.js +++ b/exp_scenario/test/controller.test.js @@ -11,8 +11,10 @@ const { ControllerPlugin: RolesPlugin } = require("@expcluster/roles/dist/node/c const { ControllerPlugin: GroupsPlugin } = require("@expcluster/permission-groups/dist/node/controller"); const { GroupRecord, GroupPermissions, RoleMappingRecord } = require("@expcluster/permission-groups"); -// Importing this defines the permissions the seed grants -require("../dist/node/permissions"); +const { plugin } = require("../dist/node/index"); + +// Registering the plugin defines the permissions the seed grants +lib.registerPluginPermissions([plugin]); // The controller validates message classes against the link registry for (const Message of [messages.SeedRequest, ...roles.plugin.messages, ...groups.plugin.messages]) { diff --git a/exp_scenario/test/seed.test.js b/exp_scenario/test/seed.test.js index 87e198a6..f1c89203 100644 --- a/exp_scenario/test/seed.test.js +++ b/exp_scenario/test/seed.test.js @@ -3,8 +3,10 @@ const t = require("tap"); const lib = require("@clusterio/lib"); const { seedRoles, seedGroups, flattenSeedPermissions } = require("../dist/node/seed"); -// Importing this defines the permissions the seed grants -require("../dist/node/permissions"); +const { plugin } = require("../dist/node/index"); + +// Registering the plugin defines the permissions the seed grants +lib.registerPluginPermissions([plugin]); t.test("seedRoles[] grant only defined permissions", t2 => { for (const role of seedRoles) { diff --git a/exp_scenario/tsconfig.browser.json b/exp_scenario/tsconfig.browser.json index 1e3889e7..b67577c5 100644 --- a/exp_scenario/tsconfig.browser.json +++ b/exp_scenario/tsconfig.browser.json @@ -1,4 +1,4 @@ { "extends": "../tsconfig.browser.json", - "include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ], + "include": [ "web/**/*.tsx", "web/**/*.ts", "index.ts", "messages.ts", "permissions.ts", "package.json" ], }