mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 15:24:52 +09:00
Define permissions on PluginDeclaration (#988)
Adds a permissions array to PluginDeclaration, a type-level Permissions registry that plugins extend with module augmentation, and registerPluginPermissions() called from all entrypoints. checkPermission and the web UI hasPermission helpers now take PermissionName so typos are compile errors. Core definitions move into a corePermissions array that the interface derives its keys from. Closes #987 Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com>
This commit is contained in:
23 files changed
+758
-575
No files matched your search
@@ -10,6 +10,7 @@ The plugin classes have pre-defined hooks that are called during various stages
|
||||
- [Defining the plugin class](#defining-the-plugin-class)
|
||||
- [Logging Messages](#logging-messages)
|
||||
- [Plugin Configuration](#plugin-configuration)
|
||||
- [Plugin Permissions](#plugin-permissions)
|
||||
- [Communicating with Factorio](#communicating-with-factorio)
|
||||
- [Defining Link Messages](#defining-link-messages)
|
||||
- [Sending Link Messages](#sending-link-messages)
|
||||
@@ -101,6 +102,10 @@ The following properties are recognized:
|
||||
Object with link messages definitions for this plugin.
|
||||
See guide for [defining link messages](#defining-link-messages) below.
|
||||
|
||||
**permissions**:
|
||||
Array of permission definitions for this plugin.
|
||||
See [Plugin Permissions](#plugin-permissions)
|
||||
|
||||
The optional module folder contains a Clusterio module that will be patched into the save when the plugin is loaded.
|
||||
See the section on [Clusterio Modules](developing-for-clusterio.md) in the Developing for Clusterio document.
|
||||
The only restriction imposed on modules embedded into plugins is that they must be named the same as the plugin.
|
||||
@@ -249,6 +254,45 @@ async init() {
|
||||
```
|
||||
|
||||
|
||||
## Plugin Permissions
|
||||
|
||||
Permissions are granted to roles and checked on users, both on the controller and in the web UI.
|
||||
A plugin declares the permissions it uses under `permissions` in the `plugin` export.
|
||||
Names must start with the plugin name followed by a dot.
|
||||
For example in index.ts:
|
||||
|
||||
```ts
|
||||
import type * as lib from "@clusterio/lib";
|
||||
|
||||
declare module "@clusterio/lib" {
|
||||
// Extend the interface of known permission names so that
|
||||
// user.checkPermission(...) accepts our permission.
|
||||
export interface Permissions {
|
||||
"foo_frobber.frobnicate": never;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
...
|
||||
permissions: [
|
||||
{
|
||||
name: "foo_frobber.frobnicate",
|
||||
title: "Frobnicate",
|
||||
description: "Run frobnication on instances.",
|
||||
grantByDefault: false, // Whether the generated Player role is granted this permission
|
||||
},
|
||||
],
|
||||
} satisfies lib.PluginDeclaration;
|
||||
```
|
||||
|
||||
The `Permissions` interface only exists at the type level, its values are always `never` and only the keys are used.
|
||||
Adding your permission names to it gives you autocomplete and a compile error on typos when calling `user.checkPermission`, `account.hasPermission` or setting the `permission` of a web UI page.
|
||||
In JavaScript the `declare module` block is not needed.
|
||||
|
||||
Permissions declared this way are registered when the plugin is loaded.
|
||||
The older `lib.definePermission()` function still works, but a plugin should not use both for the same permission name.
|
||||
|
||||
|
||||
## Communicating with Factorio
|
||||
|
||||
For pushing data into Factorio there's RCON, which lets you send arbitrary Lua commands to invoke whatever code you want in the game.
|
||||
|
||||
@@ -402,6 +402,7 @@ async function initialize(): Promise<InitializeParameters> {
|
||||
logger.info("Loading Plugin info");
|
||||
const pluginInfos = await lib.loadPluginInfos(pluginList);
|
||||
lib.registerPluginMessages(pluginInfos);
|
||||
lib.registerPluginPermissions(pluginInfos);
|
||||
lib.addPluginConfigFields(pluginInfos);
|
||||
|
||||
const controllerConfigPath = args.config;
|
||||
|
||||
@@ -169,7 +169,7 @@ export default class ControlConnection extends BaseConnection {
|
||||
}
|
||||
|
||||
if (typeof permission === "string") {
|
||||
this.user.checkPermission(permission);
|
||||
this.user.checkPermission(permission as lib.PermissionName);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import {
|
||||
IUser, Role, SubscribableDatastore,
|
||||
PermissionError, permissions,
|
||||
PermissionError, PermissionName, permissions,
|
||||
} from "@clusterio/lib";
|
||||
|
||||
import UserRecord from "./UserRecord.js";
|
||||
@@ -126,7 +126,7 @@ export default class User extends UserRecord implements IUser {
|
||||
this.saveRecord();
|
||||
}
|
||||
|
||||
checkPermission(permission: string) {
|
||||
checkPermission(permission: PermissionName) {
|
||||
if (!permissions.has(permission)) {
|
||||
throw new Error(`permission ${permission} does not exist`);
|
||||
}
|
||||
|
||||
@@ -1,38 +1,6 @@
|
||||
import * as lib from "@clusterio/lib";
|
||||
//%if multi_context
|
||||
import Messages from "./messages.js";
|
||||
|
||||
lib.definePermission({
|
||||
name: "__plugin_name__.example.permission.event",
|
||||
title: "Example permission event",
|
||||
description: "Example Description. Event. Change me in index.js",
|
||||
grantByDefault: true,
|
||||
});
|
||||
|
||||
lib.definePermission({
|
||||
name: "__plugin_name__.example.permission.request",
|
||||
title: "Example permission request",
|
||||
description: "Example Description. Request. Change me in index.js",
|
||||
grantByDefault: true,
|
||||
});
|
||||
//%endif
|
||||
//%if controller & web // Subscribing requires web content and the controller
|
||||
|
||||
lib.definePermission({
|
||||
name: "__plugin_name__.example.permission.subscribe",
|
||||
title: "Example permission subscribe",
|
||||
description: "Example Description. Subscribe. Change me in index.js",
|
||||
grantByDefault: true,
|
||||
});
|
||||
//%endif
|
||||
//%if web
|
||||
|
||||
lib.definePermission({
|
||||
name: "__plugin_name__.page.view",
|
||||
title: "Example page view permission",
|
||||
description: "Example Description. View. Change me in index.js",
|
||||
grantByDefault: true,
|
||||
});
|
||||
//%endif
|
||||
|
||||
export const plugin = {
|
||||
@@ -115,6 +83,43 @@ export const plugin = {
|
||||
//%if multi_context // Subscribing requires multi context
|
||||
],
|
||||
//%endif
|
||||
//%if multi_context | web
|
||||
|
||||
permissions: [
|
||||
//%endif
|
||||
//%if multi_context
|
||||
{
|
||||
name: "__plugin_name__.example.permission.event",
|
||||
title: "Example permission event",
|
||||
description: "Example Description. Event. Change me in index.js",
|
||||
grantByDefault: true,
|
||||
},
|
||||
{
|
||||
name: "__plugin_name__.example.permission.request",
|
||||
title: "Example permission request",
|
||||
description: "Example Description. Request. Change me in index.js",
|
||||
grantByDefault: true,
|
||||
},
|
||||
//%endif
|
||||
//%if controller & web // Subscribing requires web content and the controller
|
||||
{
|
||||
name: "__plugin_name__.example.permission.subscribe",
|
||||
title: "Example permission subscribe",
|
||||
description: "Example Description. Subscribe. Change me in index.js",
|
||||
grantByDefault: true,
|
||||
},
|
||||
//%endif
|
||||
//%if web
|
||||
{
|
||||
name: "__plugin_name__.page.view",
|
||||
title: "Example page view permission",
|
||||
description: "Example Description. View. Change me in index.js",
|
||||
grantByDefault: true,
|
||||
},
|
||||
//%endif
|
||||
//%if multi_context | web
|
||||
],
|
||||
//%endif
|
||||
//%if web // Web content template has an example route which is the plugin name
|
||||
|
||||
webEntrypoint: "./web",
|
||||
|
||||
@@ -57,7 +57,7 @@ export class WebPlugin extends BaseWebPlugin {
|
||||
permission: "__plugin_name__.example.permission.subscribe",
|
||||
//%endif
|
||||
//%if !controller
|
||||
permission: "__plugin_name__.example.permission.view",
|
||||
permission: "__plugin_name__.page.view",
|
||||
//%endif
|
||||
content: <MyTemplatePage/>,
|
||||
},
|
||||
|
||||
@@ -1,40 +1,8 @@
|
||||
import * as lib from "@clusterio/lib";
|
||||
//%if multi_context
|
||||
import * as Messages from "./messages.js";
|
||||
|
||||
lib.definePermission({
|
||||
name: "__plugin_name__.example.permission.event",
|
||||
title: "Example permission event",
|
||||
description: "Example Description. Event. Change me in index.ts",
|
||||
grantByDefault: true,
|
||||
});
|
||||
|
||||
lib.definePermission({
|
||||
name: "__plugin_name__.example.permission.request",
|
||||
title: "Example permission request",
|
||||
description: "Example Description. Request. Change me in index.ts",
|
||||
grantByDefault: true,
|
||||
});
|
||||
//%endif
|
||||
//%if controller & web // Subscribing requires web content and the controller
|
||||
|
||||
lib.definePermission({
|
||||
name: "__plugin_name__.example.permission.subscribe",
|
||||
title: "Example permission subscribe",
|
||||
description: "Example Description. Subscribe. Change me in index.ts",
|
||||
grantByDefault: true,
|
||||
});
|
||||
//%endif
|
||||
//%if web
|
||||
|
||||
lib.definePermission({
|
||||
name: "__plugin_name__.page.view",
|
||||
title: "Example page view permission",
|
||||
description: "Example Description. View. Change me in index.ts",
|
||||
grantByDefault: true,
|
||||
});
|
||||
//%endif
|
||||
//%if config
|
||||
//%if config | multi_context | web
|
||||
|
||||
declare module "@clusterio/lib" {
|
||||
//%endif
|
||||
@@ -58,7 +26,23 @@ declare module "@clusterio/lib" {
|
||||
"__plugin_name__.myControlField": string;
|
||||
}
|
||||
//%endif
|
||||
//%if config
|
||||
//%if multi_context | web
|
||||
export interface Permissions {
|
||||
//%endif
|
||||
//%if multi_context
|
||||
"__plugin_name__.example.permission.event": never;
|
||||
"__plugin_name__.example.permission.request": never;
|
||||
//%endif
|
||||
//%if controller & web // Subscribing requires web content and the controller
|
||||
"__plugin_name__.example.permission.subscribe": never;
|
||||
//%endif
|
||||
//%if web
|
||||
"__plugin_name__.page.view": never;
|
||||
//%endif
|
||||
//%if multi_context | web
|
||||
}
|
||||
//%endif
|
||||
//%if config | multi_context | web
|
||||
}
|
||||
//%endif
|
||||
|
||||
@@ -143,6 +127,43 @@ export const plugin: lib.PluginDeclaration = {
|
||||
//%if multi_context // Subscribing requires multi context
|
||||
],
|
||||
//%endif
|
||||
//%if multi_context | web
|
||||
|
||||
permissions: [
|
||||
//%endif
|
||||
//%if multi_context
|
||||
{
|
||||
name: "__plugin_name__.example.permission.event",
|
||||
title: "Example permission event",
|
||||
description: "Example Description. Event. Change me in index.ts",
|
||||
grantByDefault: true,
|
||||
},
|
||||
{
|
||||
name: "__plugin_name__.example.permission.request",
|
||||
title: "Example permission request",
|
||||
description: "Example Description. Request. Change me in index.ts",
|
||||
grantByDefault: true,
|
||||
},
|
||||
//%endif
|
||||
//%if controller & web // Subscribing requires web content and the controller
|
||||
{
|
||||
name: "__plugin_name__.example.permission.subscribe",
|
||||
title: "Example permission subscribe",
|
||||
description: "Example Description. Subscribe. Change me in index.ts",
|
||||
grantByDefault: true,
|
||||
},
|
||||
//%endif
|
||||
//%if web
|
||||
{
|
||||
name: "__plugin_name__.page.view",
|
||||
title: "Example page view permission",
|
||||
description: "Example Description. View. Change me in index.ts",
|
||||
grantByDefault: true,
|
||||
},
|
||||
//%endif
|
||||
//%if multi_context | web
|
||||
],
|
||||
//%endif
|
||||
//%if web // Web content template has an example route which is the plugin name
|
||||
|
||||
webEntrypoint: "./web",
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
"noEmitOnError": true,
|
||||
"outDir": "${configDir}/dist/browser",
|
||||
},
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "index.ts", "messages.ts", "package.json" ],
|
||||
}
|
||||
@@ -57,7 +57,7 @@ export class WebPlugin extends BaseWebPlugin {
|
||||
permission: "__plugin_name__.example.permission.subscribe",
|
||||
//%endif
|
||||
//%if !controller
|
||||
permission: "__plugin_name__.example.permission.view",
|
||||
permission: "__plugin_name__.page.view",
|
||||
//%endif
|
||||
content: <MyTemplatePage/>,
|
||||
},
|
||||
|
||||
@@ -108,6 +108,7 @@ export class Control extends lib.Link {
|
||||
async function loadPlugins(pluginList: Map<string, string>) {
|
||||
let pluginInfos = await lib.loadPluginInfos(pluginList);
|
||||
lib.registerPluginMessages(pluginInfos);
|
||||
lib.registerPluginPermissions(pluginInfos);
|
||||
lib.addPluginConfigFields(pluginInfos);
|
||||
|
||||
let ctlPlugins = new Map<string, BaseCtlPlugin>();
|
||||
|
||||
@@ -163,6 +163,7 @@ async function startHost() {
|
||||
logger.info("Loading Plugin info");
|
||||
let pluginInfos = await lib.loadPluginInfos(pluginList);
|
||||
lib.registerPluginMessages(pluginInfos);
|
||||
lib.registerPluginPermissions(pluginInfos);
|
||||
lib.addPluginConfigFields(pluginInfos);
|
||||
|
||||
const hostConfigPath = args.config;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Static, Type } from "@sinclair/typebox";
|
||||
|
||||
import type { PermissionName } from "../permissions.js";
|
||||
import PlayerStats from "./PlayerStats.js";
|
||||
|
||||
/** Represents a user in the cluster */
|
||||
@@ -164,5 +164,5 @@ export interface IUser extends UserDetails {
|
||||
* @throws {Error} If the given permission does not exist.
|
||||
* @throws {libErrors.PermissionError} if the user does noh have the given permission.
|
||||
*/
|
||||
checkPermission(permission: string): void
|
||||
checkPermission(permission: PermissionName): void
|
||||
}
|
||||
+531
-473
File diff suppressed because it is too large.
Load diff
@@ -5,9 +5,9 @@
|
||||
import * as libHelpers from "./helpers.js";
|
||||
import type { Logger } from "./logging.js";
|
||||
import type { FieldDefinition } from "./config/index.js";
|
||||
import type { PermissionDefinition } from "./permissions.js";
|
||||
import type { PlayerStats } from "./data/index.js";
|
||||
|
||||
|
||||
export const PluginFeatureFlags = [
|
||||
/** The plugin requires module code to be patched into the save */
|
||||
"SavePatching",
|
||||
@@ -37,6 +37,7 @@ export type PluginDeclaration = {
|
||||
features?: (typeof PluginFeatureFlags)[number][];
|
||||
|
||||
messages?: any[];
|
||||
permissions?: PermissionDefinition[];
|
||||
routes?: string[];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Type, Static } from "@sinclair/typebox";
|
||||
import { Link, Event, EventClass, RequestHandler, WebSocketBaseConnector } from "./link/index.js";
|
||||
import { Address, MessageRequest, IUser, JsonBoolean, StringEnum } from "./data/index.js";
|
||||
import isDeepStrictEqual from "#is_deep_strict_equal";
|
||||
import type { PermissionName } from "./permissions.js";
|
||||
import { RequestError } from "./errors.js";
|
||||
import { logger } from "./logging.js";
|
||||
|
||||
@@ -155,7 +156,7 @@ export class SubscriptionRequest {
|
||||
const entry = Link._eventsByName.get(data[0]);
|
||||
if (entry && entry.Event.permission) {
|
||||
if (typeof entry.Event.permission === "string") {
|
||||
user.checkPermission(entry.Event.permission);
|
||||
user.checkPermission(entry.Event.permission as PermissionName);
|
||||
} else {
|
||||
entry.Event.permission(user, message);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type React from "react";
|
||||
import type { AccountRole, FieldDefinition, Logger, PluginWebpackEnvInfo } from "@clusterio/lib";
|
||||
import type { AccountRole, FieldDefinition, Logger, PermissionName, PluginWebpackEnvInfo } from "@clusterio/lib";
|
||||
import type { Control } from "./util/websocket";
|
||||
|
||||
/**
|
||||
@@ -37,11 +37,11 @@ export type UserAccount = {
|
||||
/** Roles of the corrently logged in account. */
|
||||
roles: AccountRole[];
|
||||
/** Check if the currently logged in account has the given permission. */
|
||||
hasPermission: (permission: string) => boolean | null;
|
||||
hasPermission: (permission: PermissionName) => boolean | null;
|
||||
/** Check if the currently logged in account has any of the given permissions. */
|
||||
hasAnyPermission: (...permissions: string[]) => boolean | null;
|
||||
hasAnyPermission: (...permissions: PermissionName[]) => boolean | null;
|
||||
/** Check if the currently logged in account has all of given permissions. */
|
||||
hasAllPermission: (...permissions: string[]) => boolean | null;
|
||||
hasAllPermission: (...permissions: PermissionName[]) => boolean | null;
|
||||
/** Logs out of the web interface. */
|
||||
logOut: () => void;
|
||||
};
|
||||
@@ -75,7 +75,7 @@ export interface PluginPage {
|
||||
/**
|
||||
* Permission to access page. function are expected to throw an error if access is deny.
|
||||
*/
|
||||
permission?: string | ((account: UserAccount) => (boolean|null));
|
||||
permission?: PermissionName | ((account: UserAccount) => (boolean|null));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -136,6 +136,7 @@ export default async function bootstrap() {
|
||||
}));
|
||||
let [pluginInfos, pluginSetKey] = await loadPluginInfos();
|
||||
lib.registerPluginMessages(pluginInfos);
|
||||
lib.registerPluginPermissions(pluginInfos);
|
||||
lib.addPluginConfigFields(pluginInfos);
|
||||
|
||||
let wsUrl = new URL(webRoot, document.location.href);
|
||||
|
||||
@@ -22,13 +22,13 @@ export function useAccount(): UserAccount {
|
||||
};
|
||||
}, [control]);
|
||||
|
||||
function checkPermissionExists(permission: string) {
|
||||
function checkPermissionExists(permission: lib.PermissionName) {
|
||||
if (!lib.permissions.has(permission)) {
|
||||
throw new Error(`permission ${permission} does not exist`);
|
||||
}
|
||||
}
|
||||
|
||||
function hasPermission(permission: string): boolean {
|
||||
function hasPermission(permission: lib.PermissionName): boolean {
|
||||
for (let role of roles) {
|
||||
if (role.permissions.includes("core.admin") || role.permissions.includes(permission)) {
|
||||
return true;
|
||||
@@ -40,14 +40,14 @@ export function useAccount(): UserAccount {
|
||||
return {
|
||||
name,
|
||||
roles,
|
||||
hasPermission(permission: string): boolean | null {
|
||||
hasPermission(permission: lib.PermissionName): boolean | null {
|
||||
if (!roles) {
|
||||
return null;
|
||||
}
|
||||
checkPermissionExists(permission);
|
||||
return hasPermission(permission);
|
||||
},
|
||||
hasAnyPermission(...permissions: string[]): boolean | null {
|
||||
hasAnyPermission(...permissions: lib.PermissionName[]): boolean | null {
|
||||
if (!roles) {
|
||||
return null;
|
||||
}
|
||||
@@ -61,7 +61,7 @@ export function useAccount(): UserAccount {
|
||||
}
|
||||
return false;
|
||||
},
|
||||
hasAllPermission(...permissions: string[]): boolean | null {
|
||||
hasAllPermission(...permissions: lib.PermissionName[]): boolean | null {
|
||||
if (!roles) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -8,15 +8,11 @@ declare module "@clusterio/lib" {
|
||||
export interface ControllerConfigFields {
|
||||
"inventory_sync.player_lock_timeout": number;
|
||||
}
|
||||
export interface Permissions {
|
||||
"inventory_sync.inventory.view": never;
|
||||
}
|
||||
}
|
||||
|
||||
lib.definePermission({
|
||||
name: "inventory_sync.inventory.view",
|
||||
title: "View player inventories",
|
||||
description: "View player inventories",
|
||||
grantByDefault: true,
|
||||
});
|
||||
|
||||
export const plugin: lib.PluginDeclaration = {
|
||||
name: "inventory_sync",
|
||||
title: "Inventory sync",
|
||||
@@ -57,6 +53,14 @@ export const plugin: lib.PluginDeclaration = {
|
||||
messages.DownloadRequest,
|
||||
messages.DatabaseStatsRequest,
|
||||
],
|
||||
permissions: [
|
||||
{
|
||||
name: "inventory_sync.inventory.view",
|
||||
title: "View player inventories",
|
||||
description: "View player inventories",
|
||||
grantByDefault: true,
|
||||
},
|
||||
],
|
||||
webEntrypoint: "./web",
|
||||
routes: ["/inventory"],
|
||||
};
|
||||
@@ -4,5 +4,5 @@
|
||||
{ "path": "../../packages/lib/tsconfig.browser.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "index.ts", "messages.ts", "package.json" ],
|
||||
}
|
||||
@@ -9,15 +9,11 @@ declare module "@clusterio/lib" {
|
||||
export interface InstanceConfigFields {
|
||||
"subspace_storage.log_item_transfers": boolean;
|
||||
}
|
||||
export interface Permissions {
|
||||
"subspace_storage.storage.view": never;
|
||||
}
|
||||
}
|
||||
|
||||
lib.definePermission({
|
||||
name: "subspace_storage.storage.view",
|
||||
title: "View Subspace Storage",
|
||||
description: "View the items and fluids stored in the shared subspace.",
|
||||
grantByDefault: true,
|
||||
});
|
||||
|
||||
export const plugin: lib.PluginDeclaration = {
|
||||
name: "subspace_storage",
|
||||
title: "Subspace Storage",
|
||||
@@ -60,6 +56,14 @@ export const plugin: lib.PluginDeclaration = {
|
||||
messages.UpdateStorageEvent,
|
||||
messages.SetStorageSubscriptionRequest,
|
||||
],
|
||||
permissions: [
|
||||
{
|
||||
name: "subspace_storage.storage.view",
|
||||
title: "View Subspace Storage",
|
||||
description: "View the items and fluids stored in the shared subspace.",
|
||||
grantByDefault: true,
|
||||
},
|
||||
],
|
||||
webEntrypoint: "./web",
|
||||
routes: ["/storage"],
|
||||
};
|
||||
@@ -4,5 +4,5 @@
|
||||
{ "path": "../../packages/lib/tsconfig.browser.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "index.ts", "messages.ts", "package.json" ],
|
||||
}
|
||||
@@ -43,4 +43,45 @@ describe("lib/users", function() {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("registerPluginPermissions()", function() {
|
||||
it("should define the permissions declared by plugins", function() {
|
||||
lib.registerPluginPermissions([
|
||||
{ name: "foo", permissions: [
|
||||
{ name: "foo.first", title: "First", description: "The first" },
|
||||
{ name: "foo.second", title: "Second", description: "The second", grantByDefault: true },
|
||||
]},
|
||||
{ name: "bar" },
|
||||
{ name: "baz", permissions: [] },
|
||||
]);
|
||||
assert(lib.permissions.has("foo.first"), "foo.first was not defined");
|
||||
assert.equal(lib.permissions.get("foo.first").grantByDefault, false);
|
||||
assert(lib.permissions.has("foo.second"), "foo.second was not defined");
|
||||
assert.equal(lib.permissions.get("foo.second").grantByDefault, true);
|
||||
});
|
||||
|
||||
it("should throw if the name does not start with the plugin name", function() {
|
||||
assert.throws(
|
||||
() => lib.registerPluginPermissions([
|
||||
{ name: "foo", permissions: [{ name: "bar.spam", title: "Spam", description: "Spam" }] },
|
||||
]),
|
||||
new Error("Expected name of permission 'bar.spam' for foo to start with 'foo.'")
|
||||
);
|
||||
assert.throws(
|
||||
() => lib.registerPluginPermissions([
|
||||
{ name: "foo", permissions: [{ title: "Spam", description: "Spam" }] },
|
||||
]),
|
||||
new Error("Expected name of permission 'undefined' for foo to start with 'foo.'")
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw on already defined permission", function() {
|
||||
assert.throws(
|
||||
() => lib.registerPluginPermissions([
|
||||
{ name: "foo", permissions: [{ name: "foo.first", title: "First", description: "The first" }] },
|
||||
]),
|
||||
new Error("Permission 'foo.first' is already defined")
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user