node-opcua-role-set-admin 2.174.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022-2024 Sterfive SAS - 833264583 RCS ORLEANS - France (https://www.sterfive.com)
4
+
5
+ Copyright (c) 2014-2022 Etienne Rossignon
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
8
+ this software and associated documentation files (the "Software"), to deal in
9
+ the Software without restriction, including without limitation the rights to
10
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11
+ the Software, and to permit persons to whom the Software is furnished to do so,
12
+ subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # node-opcua-role-set-admin
2
+
3
+ A small command-line tool to administer the **RoleSet** and **User Management**
4
+ (OPC 10000-18) of any OPC UA server, built on
5
+ [`node-opcua-role-set-client`](../node-opcua-role-set-client). It connects over a
6
+ real session and drives the standard Methods, so it works against any compliant
7
+ server.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install -g node-opcua-role-set-admin
13
+ ```
14
+
15
+ This installs the `role-set-admin` command.
16
+
17
+ ## Usage
18
+
19
+ ```bash
20
+ role-set-admin -e opc.tcp://host:4840 -u admin -p *** list-roles
21
+ role-set-admin -e opc.tcp://host:4840 -u admin show-role Operator # password prompted
22
+ role-set-admin -e ... -u admin -p *** add-identity Operator joe
23
+ role-set-admin -e ... -u admin -p *** add-user alice s3cret -r Operator
24
+ role-set-admin -e ... -u admin -p *** remove-user alice
25
+ ```
26
+
27
+ By default it connects with **SignAndEncrypt / Basic256Sha256** (most management
28
+ Methods require an encrypted channel and the `SecurityAdmin` Role). Use
29
+ `--insecure` for `None/None`.
30
+
31
+ If `--password` is omitted (but `--username` is given), the password is read from
32
+ the terminal with **hidden input** — so it never lands in your shell history or
33
+ the process list.
34
+
35
+ ### Global options
36
+
37
+ | Option | Description |
38
+ |--------|-------------|
39
+ | `-e, --endpoint <url>` | OPC UA endpoint (required), e.g. `opc.tcp://host:4840`. |
40
+ | `-u, --username <name>` | User name (omit for anonymous). |
41
+ | `-p, --password <pw>` | Password (prompted with hidden input if omitted). |
42
+ | `--insecure` | Connect without security (`None/None`). |
43
+
44
+ ### Commands
45
+
46
+ | Command | Description |
47
+ |---------|-------------|
48
+ | `list-roles` | List all Roles and their identities. |
49
+ | `show-role <name>` | Show one Role's identities. |
50
+ | `add-role <name>` | Add a custom Role. |
51
+ | `remove-role <name>` | Remove a custom Role. |
52
+ | `add-identity <role> <user>` | Map a UserName to a Role. |
53
+ | `remove-identity <role> <user>` | Unmap a UserName from a Role. |
54
+ | `list-users` | List managed users. |
55
+ | `add-user <user> <password> [-r <role...>]` | Add a user, optionally granting Roles. |
56
+ | `remove-user <user>` | Remove a user. |
57
+ | `change-password <old> <new>` | Change the connected user's password. |
58
+
59
+ ## Programmatic API
60
+
61
+ The command actions are also exported, so you can script the same administration:
62
+
63
+ ```ts
64
+ import { addIdentity, listRoles } from "node-opcua-role-set-admin";
65
+
66
+ await listRoles({ endpoint: "opc.tcp://host:4840", username: "admin", password: "***" });
67
+ await addIdentity({ endpoint, username: "admin", password: "***" }, "Operator", "joe");
68
+ ```
69
+
70
+ ## Related packages
71
+
72
+ - [`node-opcua-role-set-client`](../node-opcua-role-set-client) — client-side RoleSet/UserManagement API.
73
+ - [`node-opcua-role-set-server`](../node-opcua-role-set-server) — server-side install & enforcement.
74
+ - [`node-opcua-role-set-common`](../node-opcua-role-set-common) — shared stores, persistence, matching.
75
+ - [Role-Based Security & User Management guide](https://github.com/node-opcua/node-opcua/blob/master/documentation/role_based_security.md) — cross-package overview & getting started.
76
+
77
+ # License
78
+
79
+ Node-OPCUA is made available to you under the MIT open source license.
80
+
81
+ See [LICENSE](./LICENSE) for details.
82
+
83
+ # Copyright
84
+
85
+ Copyright (c) 2022-2026 Sterfive SAS - https://www.sterfive.com
86
+
87
+ Copyright (c) 2014-2022 Etienne Rossignon
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ /**
5
+ * @module node-opcua-role-set-admin
6
+ *
7
+ * `role-set-admin` — administer the RoleSet & User Management (OPC 10000-18) of
8
+ * any OPC UA server over a real session.
9
+ */
10
+ const commander_1 = require("commander");
11
+ const commands_js_1 = require("./commands.js");
12
+ const program = new commander_1.Command()
13
+ .name("role-set-admin")
14
+ .description("Administer the RoleSet & User Management of an OPC UA server (OPC 10000-18)")
15
+ .requiredOption("-e, --endpoint <url>", "OPC UA endpoint, e.g. opc.tcp://host:4840")
16
+ .option("-u, --username <name>", "user name (omit for anonymous)")
17
+ .option("-p, --password <password>", "password (prompted with hidden input if omitted)")
18
+ .option("--insecure", "connect without security (None/None) instead of SignAndEncrypt");
19
+ const fail = (err) => {
20
+ console.error(`error: ${err.message}`);
21
+ process.exit(1);
22
+ };
23
+ /**
24
+ * Adapt one of the {@link GlobalOpts}-first command actions to a commander
25
+ * handler. commander appends an options object (for commands with options) and
26
+ * the `Command` itself after the declared positionals — we keep only the leading
27
+ * string positionals and read the global options off the root program.
28
+ */
29
+ function run(action) {
30
+ return (...args) => {
31
+ const positionals = args.filter((a) => typeof a === "string");
32
+ action(program.opts(), ...positionals).catch(fail);
33
+ };
34
+ }
35
+ program.command("list-roles").description("list all Roles and their identities").action(run(commands_js_1.listRoles));
36
+ program.command("show-role <name>").description("show one Role's identities").action(run(commands_js_1.showRole));
37
+ program.command("add-role <name>").description("add a custom Role").action(run(commands_js_1.addRole));
38
+ program.command("remove-role <name>").description("remove a custom Role").action(run(commands_js_1.removeRole));
39
+ program.command("add-identity <role> <user>").description("map a UserName to a Role").action(run(commands_js_1.addIdentity));
40
+ program.command("remove-identity <role> <user>").description("unmap a UserName from a Role").action(run(commands_js_1.removeIdentity));
41
+ program.command("list-users").description("list managed users").action(run(commands_js_1.listUsers));
42
+ program
43
+ .command("add-user <user> <password>")
44
+ .description("add a user, optionally granting Roles")
45
+ .option("-r, --role <name...>", "Role(s) to grant", [])
46
+ .action((user, password, cmdOpts) => {
47
+ (0, commands_js_1.addUser)(program.opts(), user, password, cmdOpts.role).catch(fail);
48
+ });
49
+ program.command("remove-user <user>").description("remove a user").action(run(commands_js_1.removeUser));
50
+ program.command("change-password <old> <new>").description("change the connected user's password").action(run(commands_js_1.changePassword));
51
+ program.parse(process.argv);
52
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../source/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { type ClientSession } from "node-opcua-client";
2
+ /** Connection + identity options shared by every command. */
3
+ export interface GlobalOpts {
4
+ endpoint: string;
5
+ username?: string;
6
+ password?: string;
7
+ /** Connect without security (None/None) instead of SignAndEncrypt/Basic256Sha256. */
8
+ insecure?: boolean;
9
+ }
10
+ /** Connect, run `fn` in a session, then tear everything down. */
11
+ export declare function withSession<T>(opts: GlobalOpts, fn: (session: ClientSession) => Promise<T>): Promise<T>;
12
+ export declare function listRoles(opts: GlobalOpts): Promise<void>;
13
+ export declare function showRole(opts: GlobalOpts, name: string): Promise<void>;
14
+ export declare function addRole(opts: GlobalOpts, name: string): Promise<void>;
15
+ export declare function removeRole(opts: GlobalOpts, name: string): Promise<void>;
16
+ export declare function addIdentity(opts: GlobalOpts, roleName: string, userName: string): Promise<void>;
17
+ export declare function removeIdentity(opts: GlobalOpts, roleName: string, userName: string): Promise<void>;
18
+ export declare function listUsers(opts: GlobalOpts): Promise<void>;
19
+ export declare function addUser(opts: GlobalOpts, userName: string, password: string, roles: string[]): Promise<void>;
20
+ export declare function removeUser(opts: GlobalOpts, userName: string): Promise<void>;
21
+ export declare function changePassword(opts: GlobalOpts, oldPassword: string, newPassword: string): Promise<void>;
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.withSession = withSession;
7
+ exports.listRoles = listRoles;
8
+ exports.showRole = showRole;
9
+ exports.addRole = addRole;
10
+ exports.removeRole = removeRole;
11
+ exports.addIdentity = addIdentity;
12
+ exports.removeIdentity = removeIdentity;
13
+ exports.listUsers = listUsers;
14
+ exports.addUser = addUser;
15
+ exports.removeUser = removeUser;
16
+ exports.changePassword = changePassword;
17
+ /**
18
+ * @module node-opcua-role-set-admin
19
+ *
20
+ * The connection helper and command actions behind the `role-set-admin` CLI,
21
+ * built on `ClientRoleSet` / `ClientUserManagement`. Exported so they can also be
22
+ * driven programmatically.
23
+ */
24
+ const node_fs_1 = require("node:fs");
25
+ const node_os_1 = __importDefault(require("node:os"));
26
+ const node_path_1 = __importDefault(require("node:path"));
27
+ const node_opcua_certificate_manager_1 = require("node-opcua-certificate-manager");
28
+ const node_opcua_client_1 = require("node-opcua-client");
29
+ const node_opcua_role_set_client_1 = require("node-opcua-role-set-client");
30
+ const node_opcua_status_code_1 = require("node-opcua-status-code");
31
+ const node_opcua_types_1 = require("node-opcua-types");
32
+ const userNameRule = (userName) => new node_opcua_types_1.IdentityMappingRuleType({ criteriaType: node_opcua_types_1.IdentityCriteriaType.UserName, criteria: userName });
33
+ /**
34
+ * Resolve the password: use `--password` if given, otherwise prompt for it with
35
+ * hidden input (so it never lands in shell history or the process list). A
36
+ * non-interactive run without `--password` is an error.
37
+ */
38
+ async function resolvePassword(opts) {
39
+ if (opts.username === undefined || opts.password !== undefined) {
40
+ return opts.password; // anonymous, or password already supplied
41
+ }
42
+ if (!process.stdin.isTTY) {
43
+ throw new Error(`password required for '${opts.username}': pass --password or run in an interactive terminal`);
44
+ }
45
+ // @inquirer/prompts is ESM-only; load it lazily so this CommonJS module can use it.
46
+ const { password } = await import("@inquirer/prompts");
47
+ return password({ message: `Password for ${opts.username}:`, mask: "*" });
48
+ }
49
+ /** Connect, run `fn` in a session, then tear everything down. */
50
+ async function withSession(opts, fn) {
51
+ const password = await resolvePassword(opts);
52
+ const secure = !opts.insecure;
53
+ const pkiRoot = node_path_1.default.join(node_os_1.default.tmpdir(), `role-set-admin-${process.pid}`);
54
+ const clientCertificateManager = new node_opcua_certificate_manager_1.OPCUACertificateManager({
55
+ rootFolder: pkiRoot,
56
+ automaticallyAcceptUnknownCertificate: true
57
+ });
58
+ await clientCertificateManager.initialize();
59
+ const client = node_opcua_client_1.OPCUAClient.create({
60
+ endpointMustExist: false,
61
+ securityMode: secure ? node_opcua_client_1.MessageSecurityMode.SignAndEncrypt : node_opcua_client_1.MessageSecurityMode.None,
62
+ securityPolicy: secure ? node_opcua_client_1.SecurityPolicy.Basic256Sha256 : node_opcua_client_1.SecurityPolicy.None,
63
+ clientCertificateManager
64
+ });
65
+ await client.connect(opts.endpoint);
66
+ try {
67
+ const userIdentity = opts.username !== undefined
68
+ ? { type: node_opcua_client_1.UserTokenType.UserName, userName: opts.username, password: password ?? "" }
69
+ : undefined;
70
+ const session = await client.createSession(userIdentity);
71
+ try {
72
+ return await fn(session);
73
+ }
74
+ finally {
75
+ await session.close();
76
+ }
77
+ }
78
+ finally {
79
+ await client.disconnect();
80
+ await clientCertificateManager.dispose();
81
+ await node_fs_1.promises.rm(pkiRoot, { recursive: true, force: true }).catch(() => undefined);
82
+ }
83
+ }
84
+ /** Print a StatusCode result and mark the process failed if it is not Good. */
85
+ function reportStatus(label, statusCode) {
86
+ const ok = statusCode === node_opcua_status_code_1.StatusCodes.Good;
87
+ console.log(`${ok ? "✓" : "✗"} ${label}: ${statusCode.name}`);
88
+ if (!ok)
89
+ process.exitCode = 1;
90
+ }
91
+ async function getRoleOrThrow(roleSet, name) {
92
+ const role = await roleSet.getRole(name);
93
+ if (!role)
94
+ throw new Error(`role '${name}' not found`);
95
+ return role;
96
+ }
97
+ async function listRoles(opts) {
98
+ await withSession(opts, async (session) => {
99
+ const roleSet = new node_opcua_role_set_client_1.ClientRoleSet(session);
100
+ for (const role of await roleSet.getRoles()) {
101
+ const identities = await role.readIdentities();
102
+ const who = identities.map((i) => i.criteria).join(", ") || "—";
103
+ console.log(`${role.roleName.padEnd(20)} ${role.roleNodeId.toString().padEnd(18)} identities: ${who}`);
104
+ }
105
+ });
106
+ }
107
+ async function showRole(opts, name) {
108
+ await withSession(opts, async (session) => {
109
+ const role = await getRoleOrThrow(new node_opcua_role_set_client_1.ClientRoleSet(session), name);
110
+ console.log(`Role ${role.roleName} (${role.roleNodeId.toString()})`);
111
+ const identities = await role.readIdentities();
112
+ if (identities.length === 0) {
113
+ console.log(" (no identities)");
114
+ }
115
+ for (const i of identities) {
116
+ console.log(` - ${node_opcua_types_1.IdentityCriteriaType[i.criteriaType]}: ${i.criteria}`);
117
+ }
118
+ });
119
+ }
120
+ async function addRole(opts, name) {
121
+ await withSession(opts, async (session) => {
122
+ const { statusCode, roleNodeId } = await new node_opcua_role_set_client_1.ClientRoleSet(session).addRole(name);
123
+ reportStatus(`AddRole '${name}'${roleNodeId ? ` -> ${roleNodeId.toString()}` : ""}`, statusCode);
124
+ });
125
+ }
126
+ async function removeRole(opts, name) {
127
+ await withSession(opts, async (session) => {
128
+ const roleSet = new node_opcua_role_set_client_1.ClientRoleSet(session);
129
+ const role = await getRoleOrThrow(roleSet, name);
130
+ reportStatus(`RemoveRole '${name}'`, (await roleSet.removeRole(role.roleNodeId)).statusCode);
131
+ });
132
+ }
133
+ async function addIdentity(opts, roleName, userName) {
134
+ await withSession(opts, async (session) => {
135
+ const role = await getRoleOrThrow(new node_opcua_role_set_client_1.ClientRoleSet(session), roleName);
136
+ reportStatus(`AddIdentity ${userName} -> ${roleName}`, (await role.addIdentity(userNameRule(userName))).statusCode);
137
+ });
138
+ }
139
+ async function removeIdentity(opts, roleName, userName) {
140
+ await withSession(opts, async (session) => {
141
+ const role = await getRoleOrThrow(new node_opcua_role_set_client_1.ClientRoleSet(session), roleName);
142
+ reportStatus(`RemoveIdentity ${userName} from ${roleName}`, (await role.removeIdentity(userNameRule(userName))).statusCode);
143
+ });
144
+ }
145
+ async function listUsers(opts) {
146
+ await withSession(opts, async (session) => {
147
+ for (const u of await new node_opcua_role_set_client_1.ClientUserManagement(session).readUsers()) {
148
+ console.log(`${(u.userName ?? "").padEnd(20)} config=${u.userConfiguration} ${u.description ?? ""}`);
149
+ }
150
+ });
151
+ }
152
+ async function addUser(opts, userName, password, roles) {
153
+ await withSession(opts, async (session) => {
154
+ const um = new node_opcua_role_set_client_1.ClientUserManagement(session);
155
+ const addStatus = (await um.addUser(userName, password, node_opcua_types_1.UserConfigurationMask.None, "")).statusCode;
156
+ reportStatus(`AddUser '${userName}'`, addStatus);
157
+ if (addStatus !== node_opcua_status_code_1.StatusCodes.Good)
158
+ return;
159
+ const roleSet = new node_opcua_role_set_client_1.ClientRoleSet(session);
160
+ for (const roleName of roles) {
161
+ const role = await getRoleOrThrow(roleSet, roleName);
162
+ reportStatus(` + identity ${userName} -> ${roleName}`, (await role.addIdentity(userNameRule(userName))).statusCode);
163
+ }
164
+ });
165
+ }
166
+ async function removeUser(opts, userName) {
167
+ await withSession(opts, async (session) => {
168
+ reportStatus(`RemoveUser '${userName}'`, (await new node_opcua_role_set_client_1.ClientUserManagement(session).removeUser(userName)).statusCode);
169
+ });
170
+ }
171
+ async function changePassword(opts, oldPassword, newPassword) {
172
+ await withSession(opts, async (session) => {
173
+ reportStatus("ChangePassword", (await new node_opcua_role_set_client_1.ClientUserManagement(session).changePassword(oldPassword, newPassword)).statusCode);
174
+ });
175
+ }
176
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../source/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @module node-opcua-role-set-admin
3
+ *
4
+ * Programmatic API behind the `role-set-admin` CLI. Import these to script the
5
+ * same RoleSet / User Management administration from your own code.
6
+ */
7
+ export { addIdentity, addRole, addUser, changePassword, type GlobalOpts, listRoles, listUsers, removeIdentity, removeRole, removeUser, showRole, withSession } from "./commands.js";
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withSession = exports.showRole = exports.removeUser = exports.removeRole = exports.removeIdentity = exports.listUsers = exports.listRoles = exports.changePassword = exports.addUser = exports.addRole = exports.addIdentity = void 0;
4
+ /**
5
+ * @module node-opcua-role-set-admin
6
+ *
7
+ * Programmatic API behind the `role-set-admin` CLI. Import these to script the
8
+ * same RoleSet / User Management administration from your own code.
9
+ */
10
+ var commands_js_1 = require("./commands.js");
11
+ Object.defineProperty(exports, "addIdentity", { enumerable: true, get: function () { return commands_js_1.addIdentity; } });
12
+ Object.defineProperty(exports, "addRole", { enumerable: true, get: function () { return commands_js_1.addRole; } });
13
+ Object.defineProperty(exports, "addUser", { enumerable: true, get: function () { return commands_js_1.addUser; } });
14
+ Object.defineProperty(exports, "changePassword", { enumerable: true, get: function () { return commands_js_1.changePassword; } });
15
+ Object.defineProperty(exports, "listRoles", { enumerable: true, get: function () { return commands_js_1.listRoles; } });
16
+ Object.defineProperty(exports, "listUsers", { enumerable: true, get: function () { return commands_js_1.listUsers; } });
17
+ Object.defineProperty(exports, "removeIdentity", { enumerable: true, get: function () { return commands_js_1.removeIdentity; } });
18
+ Object.defineProperty(exports, "removeRole", { enumerable: true, get: function () { return commands_js_1.removeRole; } });
19
+ Object.defineProperty(exports, "removeUser", { enumerable: true, get: function () { return commands_js_1.removeUser; } });
20
+ Object.defineProperty(exports, "showRole", { enumerable: true, get: function () { return commands_js_1.showRole; } });
21
+ Object.defineProperty(exports, "withSession", { enumerable: true, get: function () { return commands_js_1.withSession; } });
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,6CAauB;AAZnB,0GAAA,WAAW,OAAA;AACX,sGAAA,OAAO,OAAA;AACP,sGAAA,OAAO,OAAA;AACP,6GAAA,cAAc,OAAA;AAEd,wGAAA,SAAS,OAAA;AACT,wGAAA,SAAS,OAAA;AACT,6GAAA,cAAc,OAAA;AACd,yGAAA,UAAU,OAAA;AACV,yGAAA,UAAU,OAAA;AACV,uGAAA,QAAQ,OAAA;AACR,0GAAA,WAAW,OAAA"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "node-opcua-role-set-admin",
3
+ "version": "2.174.0",
4
+ "description": "pure nodejs OPCUA SDK - role-set-admin CLI to administer RoleSet & User Management (OPC 10000-18)",
5
+ "scripts": {
6
+ "build": "tsc -b",
7
+ "clean": "npx rimraf -g node_modules dist *.tsbuildinfo",
8
+ "role-set-admin": "tsx source/cli.ts"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "bin": {
13
+ "role-set-admin": "./dist/cli.js"
14
+ },
15
+ "devEngines": {
16
+ "node": ">=18.x",
17
+ "npm": ">=10.x"
18
+ },
19
+ "dependencies": {
20
+ "@inquirer/prompts": "8.5.2",
21
+ "commander": "^12.1.0",
22
+ "node-opcua-certificate-manager": "2.174.0",
23
+ "node-opcua-client": "2.174.0",
24
+ "node-opcua-role-set-client": "2.174.0",
25
+ "node-opcua-status-code": "2.173.0",
26
+ "node-opcua-types": "2.174.0"
27
+ },
28
+ "devDependencies": {
29
+ "tsx": "^4.22.4"
30
+ },
31
+ "author": "Etienne Rossignon",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git://github.com/node-opcua/node-opcua.git"
36
+ },
37
+ "keywords": [
38
+ "OPCUA",
39
+ "opcua",
40
+ "m2m",
41
+ "iot",
42
+ "opc ua",
43
+ "internet of things",
44
+ "role",
45
+ "rbac",
46
+ "cli"
47
+ ],
48
+ "homepage": "http://node-opcua.github.io/",
49
+ "files": [
50
+ "dist",
51
+ "source"
52
+ ]
53
+ }
package/source/cli.ts ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @module node-opcua-role-set-admin
4
+ *
5
+ * `role-set-admin` — administer the RoleSet & User Management (OPC 10000-18) of
6
+ * any OPC UA server over a real session.
7
+ */
8
+ import { Command } from "commander";
9
+ import {
10
+ addIdentity,
11
+ addRole,
12
+ addUser,
13
+ changePassword,
14
+ type GlobalOpts,
15
+ listRoles,
16
+ listUsers,
17
+ removeIdentity,
18
+ removeRole,
19
+ removeUser,
20
+ showRole
21
+ } from "./commands.js";
22
+
23
+ const program = new Command()
24
+ .name("role-set-admin")
25
+ .description("Administer the RoleSet & User Management of an OPC UA server (OPC 10000-18)")
26
+ .requiredOption("-e, --endpoint <url>", "OPC UA endpoint, e.g. opc.tcp://host:4840")
27
+ .option("-u, --username <name>", "user name (omit for anonymous)")
28
+ .option("-p, --password <password>", "password (prompted with hidden input if omitted)")
29
+ .option("--insecure", "connect without security (None/None) instead of SignAndEncrypt");
30
+
31
+ const fail = (err: Error): never => {
32
+ console.error(`error: ${err.message}`);
33
+ process.exit(1);
34
+ };
35
+
36
+ /**
37
+ * Adapt one of the {@link GlobalOpts}-first command actions to a commander
38
+ * handler. commander appends an options object (for commands with options) and
39
+ * the `Command` itself after the declared positionals — we keep only the leading
40
+ * string positionals and read the global options off the root program.
41
+ */
42
+ function run(action: (opts: GlobalOpts, ...args: string[]) => Promise<void>) {
43
+ return (...args: unknown[]) => {
44
+ const positionals = args.filter((a): a is string => typeof a === "string");
45
+ action(program.opts<GlobalOpts>(), ...positionals).catch(fail);
46
+ };
47
+ }
48
+
49
+ program.command("list-roles").description("list all Roles and their identities").action(run(listRoles));
50
+ program.command("show-role <name>").description("show one Role's identities").action(run(showRole));
51
+ program.command("add-role <name>").description("add a custom Role").action(run(addRole));
52
+ program.command("remove-role <name>").description("remove a custom Role").action(run(removeRole));
53
+ program.command("add-identity <role> <user>").description("map a UserName to a Role").action(run(addIdentity));
54
+ program.command("remove-identity <role> <user>").description("unmap a UserName from a Role").action(run(removeIdentity));
55
+ program.command("list-users").description("list managed users").action(run(listUsers));
56
+ program
57
+ .command("add-user <user> <password>")
58
+ .description("add a user, optionally granting Roles")
59
+ .option("-r, --role <name...>", "Role(s) to grant", [])
60
+ .action((user: string, password: string, cmdOpts: { role: string[] }) => {
61
+ addUser(program.opts<GlobalOpts>(), user, password, cmdOpts.role).catch(fail);
62
+ });
63
+ program.command("remove-user <user>").description("remove a user").action(run(removeUser));
64
+ program.command("change-password <old> <new>").description("change the connected user's password").action(run(changePassword));
65
+
66
+ program.parse(process.argv);
@@ -0,0 +1,184 @@
1
+ /**
2
+ * @module node-opcua-role-set-admin
3
+ *
4
+ * The connection helper and command actions behind the `role-set-admin` CLI,
5
+ * built on `ClientRoleSet` / `ClientUserManagement`. Exported so they can also be
6
+ * driven programmatically.
7
+ */
8
+ import { promises as fs } from "node:fs";
9
+ import os from "node:os";
10
+ import path from "node:path";
11
+ import { OPCUACertificateManager } from "node-opcua-certificate-manager";
12
+ import { type ClientSession, MessageSecurityMode, OPCUAClient, SecurityPolicy, UserTokenType } from "node-opcua-client";
13
+ import { ClientRoleSet, ClientUserManagement } from "node-opcua-role-set-client";
14
+ import { type StatusCode, StatusCodes } from "node-opcua-status-code";
15
+ import { IdentityCriteriaType, IdentityMappingRuleType, UserConfigurationMask } from "node-opcua-types";
16
+
17
+ /** Connection + identity options shared by every command. */
18
+ export interface GlobalOpts {
19
+ endpoint: string;
20
+ username?: string;
21
+ password?: string;
22
+ /** Connect without security (None/None) instead of SignAndEncrypt/Basic256Sha256. */
23
+ insecure?: boolean;
24
+ }
25
+
26
+ const userNameRule = (userName: string) =>
27
+ new IdentityMappingRuleType({ criteriaType: IdentityCriteriaType.UserName, criteria: userName });
28
+
29
+ /**
30
+ * Resolve the password: use `--password` if given, otherwise prompt for it with
31
+ * hidden input (so it never lands in shell history or the process list). A
32
+ * non-interactive run without `--password` is an error.
33
+ */
34
+ async function resolvePassword(opts: GlobalOpts): Promise<string | undefined> {
35
+ if (opts.username === undefined || opts.password !== undefined) {
36
+ return opts.password; // anonymous, or password already supplied
37
+ }
38
+ if (!process.stdin.isTTY) {
39
+ throw new Error(`password required for '${opts.username}': pass --password or run in an interactive terminal`);
40
+ }
41
+ // @inquirer/prompts is ESM-only; load it lazily so this CommonJS module can use it.
42
+ const { password } = await import("@inquirer/prompts");
43
+ return password({ message: `Password for ${opts.username}:`, mask: "*" });
44
+ }
45
+
46
+ /** Connect, run `fn` in a session, then tear everything down. */
47
+ export async function withSession<T>(opts: GlobalOpts, fn: (session: ClientSession) => Promise<T>): Promise<T> {
48
+ const password = await resolvePassword(opts);
49
+ const secure = !opts.insecure;
50
+ const pkiRoot = path.join(os.tmpdir(), `role-set-admin-${process.pid}`);
51
+ const clientCertificateManager = new OPCUACertificateManager({
52
+ rootFolder: pkiRoot,
53
+ automaticallyAcceptUnknownCertificate: true
54
+ });
55
+ await clientCertificateManager.initialize();
56
+
57
+ const client = OPCUAClient.create({
58
+ endpointMustExist: false,
59
+ securityMode: secure ? MessageSecurityMode.SignAndEncrypt : MessageSecurityMode.None,
60
+ securityPolicy: secure ? SecurityPolicy.Basic256Sha256 : SecurityPolicy.None,
61
+ clientCertificateManager
62
+ });
63
+ await client.connect(opts.endpoint);
64
+ try {
65
+ const userIdentity =
66
+ opts.username !== undefined
67
+ ? { type: UserTokenType.UserName as const, userName: opts.username, password: password ?? "" }
68
+ : undefined;
69
+ const session = await client.createSession(userIdentity);
70
+ try {
71
+ return await fn(session);
72
+ } finally {
73
+ await session.close();
74
+ }
75
+ } finally {
76
+ await client.disconnect();
77
+ await clientCertificateManager.dispose();
78
+ await fs.rm(pkiRoot, { recursive: true, force: true }).catch(() => undefined);
79
+ }
80
+ }
81
+
82
+ /** Print a StatusCode result and mark the process failed if it is not Good. */
83
+ function reportStatus(label: string, statusCode: StatusCode): void {
84
+ const ok = statusCode === StatusCodes.Good;
85
+ console.log(`${ok ? "✓" : "✗"} ${label}: ${statusCode.name}`);
86
+ if (!ok) process.exitCode = 1;
87
+ }
88
+
89
+ async function getRoleOrThrow(roleSet: ClientRoleSet, name: string) {
90
+ const role = await roleSet.getRole(name);
91
+ if (!role) throw new Error(`role '${name}' not found`);
92
+ return role;
93
+ }
94
+
95
+ export async function listRoles(opts: GlobalOpts): Promise<void> {
96
+ await withSession(opts, async (session) => {
97
+ const roleSet = new ClientRoleSet(session);
98
+ for (const role of await roleSet.getRoles()) {
99
+ const identities = await role.readIdentities();
100
+ const who = identities.map((i) => i.criteria).join(", ") || "—";
101
+ console.log(`${role.roleName.padEnd(20)} ${role.roleNodeId.toString().padEnd(18)} identities: ${who}`);
102
+ }
103
+ });
104
+ }
105
+
106
+ export async function showRole(opts: GlobalOpts, name: string): Promise<void> {
107
+ await withSession(opts, async (session) => {
108
+ const role = await getRoleOrThrow(new ClientRoleSet(session), name);
109
+ console.log(`Role ${role.roleName} (${role.roleNodeId.toString()})`);
110
+ const identities = await role.readIdentities();
111
+ if (identities.length === 0) {
112
+ console.log(" (no identities)");
113
+ }
114
+ for (const i of identities) {
115
+ console.log(` - ${IdentityCriteriaType[i.criteriaType]}: ${i.criteria}`);
116
+ }
117
+ });
118
+ }
119
+
120
+ export async function addRole(opts: GlobalOpts, name: string): Promise<void> {
121
+ await withSession(opts, async (session) => {
122
+ const { statusCode, roleNodeId } = await new ClientRoleSet(session).addRole(name);
123
+ reportStatus(`AddRole '${name}'${roleNodeId ? ` -> ${roleNodeId.toString()}` : ""}`, statusCode);
124
+ });
125
+ }
126
+
127
+ export async function removeRole(opts: GlobalOpts, name: string): Promise<void> {
128
+ await withSession(opts, async (session) => {
129
+ const roleSet = new ClientRoleSet(session);
130
+ const role = await getRoleOrThrow(roleSet, name);
131
+ reportStatus(`RemoveRole '${name}'`, (await roleSet.removeRole(role.roleNodeId)).statusCode);
132
+ });
133
+ }
134
+
135
+ export async function addIdentity(opts: GlobalOpts, roleName: string, userName: string): Promise<void> {
136
+ await withSession(opts, async (session) => {
137
+ const role = await getRoleOrThrow(new ClientRoleSet(session), roleName);
138
+ reportStatus(`AddIdentity ${userName} -> ${roleName}`, (await role.addIdentity(userNameRule(userName))).statusCode);
139
+ });
140
+ }
141
+
142
+ export async function removeIdentity(opts: GlobalOpts, roleName: string, userName: string): Promise<void> {
143
+ await withSession(opts, async (session) => {
144
+ const role = await getRoleOrThrow(new ClientRoleSet(session), roleName);
145
+ reportStatus(`RemoveIdentity ${userName} from ${roleName}`, (await role.removeIdentity(userNameRule(userName))).statusCode);
146
+ });
147
+ }
148
+
149
+ export async function listUsers(opts: GlobalOpts): Promise<void> {
150
+ await withSession(opts, async (session) => {
151
+ for (const u of await new ClientUserManagement(session).readUsers()) {
152
+ console.log(`${(u.userName ?? "").padEnd(20)} config=${u.userConfiguration} ${u.description ?? ""}`);
153
+ }
154
+ });
155
+ }
156
+
157
+ export async function addUser(opts: GlobalOpts, userName: string, password: string, roles: string[]): Promise<void> {
158
+ await withSession(opts, async (session) => {
159
+ const um = new ClientUserManagement(session);
160
+ const addStatus = (await um.addUser(userName, password, UserConfigurationMask.None, "")).statusCode;
161
+ reportStatus(`AddUser '${userName}'`, addStatus);
162
+ if (addStatus !== StatusCodes.Good) return;
163
+ const roleSet = new ClientRoleSet(session);
164
+ for (const roleName of roles) {
165
+ const role = await getRoleOrThrow(roleSet, roleName);
166
+ reportStatus(` + identity ${userName} -> ${roleName}`, (await role.addIdentity(userNameRule(userName))).statusCode);
167
+ }
168
+ });
169
+ }
170
+
171
+ export async function removeUser(opts: GlobalOpts, userName: string): Promise<void> {
172
+ await withSession(opts, async (session) => {
173
+ reportStatus(`RemoveUser '${userName}'`, (await new ClientUserManagement(session).removeUser(userName)).statusCode);
174
+ });
175
+ }
176
+
177
+ export async function changePassword(opts: GlobalOpts, oldPassword: string, newPassword: string): Promise<void> {
178
+ await withSession(opts, async (session) => {
179
+ reportStatus(
180
+ "ChangePassword",
181
+ (await new ClientUserManagement(session).changePassword(oldPassword, newPassword)).statusCode
182
+ );
183
+ });
184
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @module node-opcua-role-set-admin
3
+ *
4
+ * Programmatic API behind the `role-set-admin` CLI. Import these to script the
5
+ * same RoleSet / User Management administration from your own code.
6
+ */
7
+ export {
8
+ addIdentity,
9
+ addRole,
10
+ addUser,
11
+ changePassword,
12
+ type GlobalOpts,
13
+ listRoles,
14
+ listUsers,
15
+ removeIdentity,
16
+ removeRole,
17
+ removeUser,
18
+ showRole,
19
+ withSession
20
+ } from "./commands.js";