node-opcua-role-set-server 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.
Files changed (55) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +188 -0
  3. package/dist/audit.d.ts +33 -0
  4. package/dist/audit.js +28 -0
  5. package/dist/audit.js.map +1 -0
  6. package/dist/bind_restriction_methods.d.ts +31 -0
  7. package/dist/bind_restriction_methods.js +94 -0
  8. package/dist/bind_restriction_methods.js.map +1 -0
  9. package/dist/bind_role_methods.d.ts +48 -0
  10. package/dist/bind_role_methods.js +143 -0
  11. package/dist/bind_role_methods.js.map +1 -0
  12. package/dist/bind_user_management.d.ts +58 -0
  13. package/dist/bind_user_management.js +153 -0
  14. package/dist/bind_user_management.js.map +1 -0
  15. package/dist/harden.d.ts +35 -0
  16. package/dist/harden.js +28 -0
  17. package/dist/harden.js.map +1 -0
  18. package/dist/index.d.ts +13 -0
  19. package/dist/index.js +35 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/install_role_based_security.d.ts +78 -0
  22. package/dist/install_role_based_security.js +41 -0
  23. package/dist/install_role_based_security.js.map +1 -0
  24. package/dist/install_role_set.d.ts +73 -0
  25. package/dist/install_role_set.js +308 -0
  26. package/dist/install_role_set.js.map +1 -0
  27. package/dist/install_user_management.d.ts +68 -0
  28. package/dist/install_user_management.js +158 -0
  29. package/dist/install_user_management.js.map +1 -0
  30. package/dist/role_set_resolver.d.ts +23 -0
  31. package/dist/role_set_resolver.js +30 -0
  32. package/dist/role_set_resolver.js.map +1 -0
  33. package/dist/security_checks.d.ts +25 -0
  34. package/dist/security_checks.js +41 -0
  35. package/dist/security_checks.js.map +1 -0
  36. package/dist/user_management_user_manager.d.ts +37 -0
  37. package/dist/user_management_user_manager.js +72 -0
  38. package/dist/user_management_user_manager.js.map +1 -0
  39. package/dist/variant_args.d.ts +13 -0
  40. package/dist/variant_args.js +18 -0
  41. package/dist/variant_args.js.map +1 -0
  42. package/package.json +54 -0
  43. package/source/audit.ts +53 -0
  44. package/source/bind_restriction_methods.ts +117 -0
  45. package/source/bind_role_methods.ts +196 -0
  46. package/source/bind_user_management.ts +215 -0
  47. package/source/harden.ts +46 -0
  48. package/source/index.ts +48 -0
  49. package/source/install_role_based_security.ts +130 -0
  50. package/source/install_role_set.ts +426 -0
  51. package/source/install_user_management.ts +250 -0
  52. package/source/role_set_resolver.ts +38 -0
  53. package/source/security_checks.ts +47 -0
  54. package/source/user_management_user_manager.ts +104 -0
  55. package/source/variant_args.ts +22 -0
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,188 @@
1
+ # node-opcua-role-set-server
2
+
3
+ Server-side **RoleSet management** for OPC UA (OPC 10000-18 — *Role-Based Security*).
4
+
5
+ This package wires the `RoleSet` object of an OPC UA server to an identity-mapping
6
+ store. It binds the `AddIdentity` / `RemoveIdentity` methods on each Role, keeps each
7
+ Role's `Identities` property in sync with the store, registers a role resolver that
8
+ maps incoming user identity tokens to roles, and optionally persists the mappings to
9
+ disk.
10
+
11
+ It builds on [`node-opcua-role-set-common`](../node-opcua-role-set-common) for the
12
+ store, persistence and well-known role NodeIds.
13
+
14
+ see http://node-opcua.github.io/
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ $ npm install node-opcua-role-set-server
20
+ ```
21
+
22
+ ## Quick start
23
+
24
+ Call `installRoleSet` **after** the server has started, when the address space is
25
+ available:
26
+
27
+ ```ts
28
+ import { OPCUAServer } from "node-opcua";
29
+ import { installRoleSet } from "node-opcua-role-set-server";
30
+
31
+ const server = new OPCUAServer({ /* ... */ });
32
+ await server.start();
33
+
34
+ const { store, restrictionStore, resolver } = await installRoleSet(server, {
35
+ persistencePath: "./config/role-set.json",
36
+ persistenceSecret: process.env.ROLE_SET_SECRET // optional: encrypt at rest
37
+ });
38
+ ```
39
+
40
+ What `installRoleSet` does:
41
+
42
+ 1. Finds the `RoleSet` node (`i=15606`) in the address space.
43
+ 2. Loads the whole configuration from the consolidated archive at `persistencePath` (if any):
44
+ identity mappings, custom Role definitions and application/endpoint restrictions.
45
+ 3. Registers a `RoleSetResolver` on `server.roleResolvers` so sessions are mapped to roles at login.
46
+ 4. For each Role in the RoleSet:
47
+ - sets the initial `Identities` (and restriction) property values from the stores;
48
+ - binds the `AddIdentity` / `RemoveIdentity` and the application/endpoint restriction methods.
49
+ 5. Binds `RoleSet.AddRole` / `RoleSet.RemoveRole` (§4.2): custom Roles are created as
50
+ `ns=1;g=<uuid>` instances of `RoleType` (collision-proof, stable across restarts);
51
+ well-known Roles cannot be removed.
52
+ 6. After every mutation, refreshes the Role variables and atomically rewrites the archive.
53
+
54
+ ### Returned values
55
+
56
+ | Field | Type | Description |
57
+ |-------|------|-------------|
58
+ | `store` | `IIdentityMappingStore` | The identity-mapping store backing the RoleSet. |
59
+ | `restrictionStore` | `IRoleRestrictionStore` | The per-Role application/endpoint restriction store. |
60
+ | `resolver` | `RoleSetResolver` | The resolver registered on `server.roleResolvers`. |
61
+
62
+ You can mutate `store` directly (e.g. to seed default mappings); call sites that go
63
+ through the bound methods automatically persist, but direct store changes are not
64
+ written to the archive until the next method-driven mutation.
65
+
66
+ ## User Management & one-call setup
67
+
68
+ This package also installs **User Management** (§5) and offers a one-call helper
69
+ that wires roles and users to a *single source of truth*. The complete
70
+ end-to-end walkthrough lives in the
71
+ [Role-Based Security & User Management guide](https://github.com/node-opcua/node-opcua/blob/master/documentation/role_based_security.md);
72
+ in short:
73
+
74
+ ```ts
75
+ import { createRoleBasedSecurity } from "node-opcua-role-set-server";
76
+ import { WellKnownRoleIds } from "node-opcua-role-set-common";
77
+
78
+ const security = createRoleBasedSecurity({
79
+ users: [{ userName: "admin", password: "admin-pw1", roles: [WellKnownRoleIds.SecurityAdmin] }]
80
+ });
81
+ const server = new OPCUAServer({ /* … */ userManager: security.userManager });
82
+ await server.start();
83
+ await security.install(server, { persistencePath: "./config/role-set.json" });
84
+ ```
85
+
86
+ ## Exports
87
+
88
+ | Export | Description |
89
+ |--------|-------------|
90
+ | `createRoleBasedSecurity(options?)` | **Recommended** — one user store + one identity store behind the `userManager` bridge, with a two-phase `install(server, …)`. |
91
+ | `installRoleSet(server, options?)` | Install RoleSet management (Roles, identities, restrictions, AddRole/RemoveRole). |
92
+ | `installUserManagement(server, options?)` | Install User Management (§5): AddUser / ModifyUser / RemoveUser / ChangePassword. |
93
+ | `createUserManager(userStore, identityStore)` | Build the server `userManager` bridge from a shared user + identity store. |
94
+ | `InstallRoleSetOptions` | `{ store?, persistence?, persistencePath?, persistenceSecret? }`. |
95
+ | `InstallRoleSetResult` | `{ store, restrictionStore, resolver }`. |
96
+ | `InstallUserManagementOptions` / `InstallUserManagementResult` | User Management install options / result. |
97
+ | `IServerForRoleSet` / `IServerForUserManagement` | Minimal server shapes required. |
98
+ | `RoleSetResolver` | Adapts an `IIdentityMappingStore` to the server's `IRoleResolver` contract. |
99
+ | `make…Handler` factories | Build the Method handlers (used internally; exposed for custom binding). |
100
+ | `checkSecurityAdminAccess` / `checkEncryptedChannel` | Authorization helpers (SecurityAdmin Role / encrypted channel). |
101
+ | `raiseAuditMethodEvent` | Raise an `AuditUpdateMethodEventType` (no secrets). |
102
+
103
+ ## Security model
104
+
105
+ The sensitive RoleSet & User Management nodes are protected with the address
106
+ space's own enforcement, so the checks run in the core *before* any bound
107
+ handler (the per-Method guards are defense-in-depth):
108
+
109
+ - **Administrative Methods require `SecurityAdmin` over an encrypted channel** —
110
+ AddRole/RemoveRole, AddIdentity/RemoveIdentity, the restriction Methods and
111
+ AddUser/ModifyUser/RemoveUser. `ChangePassword` stays callable by any
112
+ authenticated user (still encrypted).
113
+ - **Non-admins cannot Browse them** (§4.4.1): each Role's `Identities` /
114
+ `Applications` / `Endpoints` Properties and config Methods, plus the `Users`
115
+ list, carry SecurityAdmin-only RolePermissions, so they are hidden and
116
+ unreadable. Role nodes themselves stay browsable.
117
+ - **`EncryptionRequired`** on those nodes → reads over a None/Sign channel return
118
+ `Bad_SecurityModeInsufficient`.
119
+ - **Disabling (ModifyUser) or removing (RemoveUser) a user terminates their live
120
+ sessions** (§5.2.6-7), not just their ability to re-authenticate.
121
+
122
+ Status codes returned by the identity Method handlers:
123
+
124
+ | Status | Condition |
125
+ |--------|-----------|
126
+ | `Good` | The identity was added / removed. |
127
+ | `BadUserAccessDenied` | The session does not hold the `SecurityAdmin` role. |
128
+ | `BadSecurityModeInsufficient` | The channel is not `SignAndEncrypt`. |
129
+ | `BadInvalidArgument` | The input argument is not an `IdentityMappingRuleType`. |
130
+ | `BadNoMatch` | `RemoveIdentity` — no matching rule was found. |
131
+ | `BadAlreadyExists` | `AddRole` — the RoleName duplicates an existing (well-known or custom) Role. |
132
+ | `BadRequestNotAllowed` | `RemoveRole` — well-known Roles cannot be removed. |
133
+
134
+ ## Persistence
135
+
136
+ When `persistencePath` is supplied, the **entire** RoleSet configuration is kept in a
137
+ single consolidated archive (identity mappings, custom Role definitions and
138
+ application/endpoint restrictions):
139
+
140
+ - the archive is loaded on install (a missing file is a no-op);
141
+ - it is **atomically** rewritten (temp file + rename) after every successful mutation, so
142
+ a crash can never leave a half-written file;
143
+ - when `persistenceSecret` is set the whole payload is encrypted at rest with AES-256-GCM
144
+ (key derived from the secret via scrypt); otherwise it is plain, human-readable JSON
145
+ with the identity mappings embedded as a base64 binary blob.
146
+
147
+ The archive format (`readArchive` / `writeArchive`, version-checked) is defined in
148
+ [`node-opcua-role-set-common`](../node-opcua-role-set-common).
149
+
150
+ ### Sharing the archive with User Management
151
+
152
+ To keep **users** (salted scrypt hashes, never clear passwords) in the *same* file as the
153
+ role configuration, create one `ArchiveStore` and pass it to both installers:
154
+
155
+ ```ts
156
+ import { ArchiveStore } from "node-opcua-role-set-common";
157
+
158
+ const persistence = new ArchiveStore("./config/role-set.json", {
159
+ secret: process.env.ROLE_SET_SECRET // optional encryption
160
+ });
161
+ await installRoleSet(server, { persistence });
162
+ await installUserManagement(server, { persistence });
163
+ ```
164
+
165
+ The coordinator gathers every registered section (identities / roles / restrictions / users)
166
+ and rewrites the one file atomically on each mutation, so neither installer clobbers the
167
+ other — a section with no provider yet keeps its last loaded value, so the result is
168
+ independent of install order. Pass only `persistencePath` instead for a role-config-only
169
+ (or users-only) file.
170
+
171
+ ## Related packages
172
+
173
+ - [`node-opcua-role-set-common`](../node-opcua-role-set-common) — shared types, identity/user stores and persistence.
174
+ - [`node-opcua-role-set-client`](../node-opcua-role-set-client) — client-side role browsing, identity & user management.
175
+ - [`node-opcua-role-set-admin`](../node-opcua-role-set-admin) — the `role-set-admin` command-line tool.
176
+ - [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.
177
+
178
+ # License
179
+
180
+ Node-OPCUA is made available to you under the MIT open source license.
181
+
182
+ See [LICENSE](./LICENSE) for details.
183
+
184
+ # Copyright
185
+
186
+ Copyright (c) 2022-2026 Sterfive SAS - https://www.sterfive.com
187
+
188
+ Copyright (c) 2014-2022 Etienne Rossignon
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @module node-opcua-role-set-server
3
+ *
4
+ * Shared helper to raise an audit method event (a subtype of
5
+ * AuditUpdateMethodEventType) on the Server Object.
6
+ */
7
+ import type { UAObject } from "node-opcua-address-space";
8
+ import { NodeId } from "node-opcua-nodeid";
9
+ import type { Variant } from "node-opcua-variant";
10
+ export interface AuditMethodEventFields {
11
+ /** The Node the audited operation acted on (Role / UserManagement Object). */
12
+ sourceNode: NodeId;
13
+ /** A short label, e.g. "Method/AddIdentity". */
14
+ sourceName: string;
15
+ /** The audited Method NodeId (omit for events not raised from a single Method call). */
16
+ methodId?: NodeId;
17
+ /** The Session user who invoked the Method. */
18
+ clientUserId: string;
19
+ /** TRUE when the operation succeeded. */
20
+ status: boolean;
21
+ /** Human-readable message (must NOT contain secrets such as passwords). */
22
+ message: string;
23
+ /**
24
+ * The Method input arguments — include ONLY when they carry no secret
25
+ * (e.g. an IdentityMappingRule). Omit for password-bearing Methods.
26
+ */
27
+ inputArguments?: Variant[];
28
+ }
29
+ /**
30
+ * Raise `eventType` on the given Server Object with the standard
31
+ * AuditUpdateMethodEventType fields. No-op if there is no Server Object.
32
+ */
33
+ export declare function raiseAuditMethodEvent(serverObject: UAObject | undefined, eventType: string, fields: AuditMethodEventFields): void;
package/dist/audit.js ADDED
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.raiseAuditMethodEvent = raiseAuditMethodEvent;
4
+ const node_opcua_nodeid_1 = require("node-opcua-nodeid");
5
+ const node_opcua_variant_1 = require("node-opcua-variant");
6
+ /**
7
+ * Raise `eventType` on the given Server Object with the standard
8
+ * AuditUpdateMethodEventType fields. No-op if there is no Server Object.
9
+ */
10
+ function raiseAuditMethodEvent(serverObject, eventType, fields) {
11
+ if (!serverObject) {
12
+ return;
13
+ }
14
+ serverObject.raiseEvent(eventType, {
15
+ actionTimeStamp: { dataType: "DateTime", value: new Date() },
16
+ status: { dataType: "Boolean", value: fields.status },
17
+ serverId: { dataType: "String", value: "" },
18
+ clientAuditEntryId: { dataType: "String", value: "" },
19
+ clientUserId: { dataType: "String", value: fields.clientUserId },
20
+ sourceNode: { dataType: "NodeId", value: fields.sourceNode },
21
+ sourceName: { dataType: "String", value: fields.sourceName },
22
+ methodId: { dataType: "NodeId", value: fields.methodId ?? node_opcua_nodeid_1.NodeId.nullNodeId },
23
+ severity: { dataType: "UInt16", value: 10 },
24
+ message: { dataType: "LocalizedText", value: fields.message },
25
+ inputArguments: { dataType: "Variant", arrayType: node_opcua_variant_1.VariantArrayType.Array, value: fields.inputArguments ?? [] }
26
+ });
27
+ }
28
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../source/audit.ts"],"names":[],"mappings":";;AAmCA,sDAiBC;AA7CD,yDAA2C;AAE3C,2DAAsD;AAsBtD;;;GAGG;AACH,SAAgB,qBAAqB,CAAC,YAAkC,EAAE,SAAiB,EAAE,MAA8B;IACvH,IAAI,CAAC,YAAY,EAAE,CAAC;QAChB,OAAO;IACX,CAAC;IACD,YAAY,CAAC,UAAU,CAAC,SAAS,EAAE;QAC/B,eAAe,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,IAAI,EAAE,EAAE;QAC5D,MAAM,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;QACrD,QAAQ,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3C,kBAAkB,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;QACrD,YAAY,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,YAAY,EAAE;QAChE,UAAU,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE;QAC5D,UAAU,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE;QAC5D,QAAQ,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,IAAI,0BAAM,CAAC,UAAU,EAAE;QAC7E,QAAQ,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3C,OAAO,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE;QAC7D,cAAc,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,qCAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,cAAc,IAAI,EAAE,EAAE;KACjH,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @module node-opcua-role-set-server
3
+ *
4
+ * Method handlers for the RoleType application/endpoint restriction Methods
5
+ * (OPC 10000-18 §4.4.7-10): AddApplication / RemoveApplication / AddEndpoint /
6
+ * RemoveEndpoint, backed by an {@link IRoleRestrictionStore}.
7
+ *
8
+ * All require the SecurityAdmin Role and an encrypted channel, and raise a
9
+ * RoleMappingRuleChangedAuditEventType (§4.5, reusing the same audit hook as the
10
+ * identity Methods).
11
+ */
12
+ import type { ISessionContext, UAMethod } from "node-opcua-address-space-base";
13
+ import type { IRoleRestrictionStore } from "node-opcua-role-set-common";
14
+ import type { CallMethodResultOptions } from "node-opcua-service-call";
15
+ import type { Variant } from "node-opcua-variant";
16
+ import type { RoleMappingRuleChangedAudit } from "./bind_role_methods.js";
17
+ export interface BindRestrictionMethodsOptions {
18
+ restrictionStore: IRoleRestrictionStore;
19
+ /** Called after every successful mutation so the caller can persist / refresh. */
20
+ onMutation?: () => Promise<void>;
21
+ /** Called after an authorized attempt to raise an audit event. */
22
+ onAudit?: (audit: RoleMappingRuleChangedAudit) => void;
23
+ }
24
+ /** AddApplication (§4.4.7). */
25
+ export declare function makeAddApplicationHandler(options: BindRestrictionMethodsOptions): (this: UAMethod, inputArguments: Variant[], context: ISessionContext) => Promise<CallMethodResultOptions>;
26
+ /** RemoveApplication (§4.4.8). */
27
+ export declare function makeRemoveApplicationHandler(options: BindRestrictionMethodsOptions): (this: UAMethod, inputArguments: Variant[], context: ISessionContext) => Promise<CallMethodResultOptions>;
28
+ /** AddEndpoint (§4.4.9). */
29
+ export declare function makeAddEndpointHandler(options: BindRestrictionMethodsOptions): (this: UAMethod, inputArguments: Variant[], context: ISessionContext) => Promise<CallMethodResultOptions>;
30
+ /** RemoveEndpoint (§4.4.10). */
31
+ export declare function makeRemoveEndpointHandler(options: BindRestrictionMethodsOptions): (this: UAMethod, inputArguments: Variant[], context: ISessionContext) => Promise<CallMethodResultOptions>;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeAddApplicationHandler = makeAddApplicationHandler;
4
+ exports.makeRemoveApplicationHandler = makeRemoveApplicationHandler;
5
+ exports.makeAddEndpointHandler = makeAddEndpointHandler;
6
+ exports.makeRemoveEndpointHandler = makeRemoveEndpointHandler;
7
+ const node_opcua_status_code_1 = require("node-opcua-status-code");
8
+ const node_opcua_types_1 = require("node-opcua-types");
9
+ const security_checks_js_1 = require("./security_checks.js");
10
+ const variant_args_js_1 = require("./variant_args.js");
11
+ function endpointFromArg(v) {
12
+ if (!v || !(v.value instanceof node_opcua_types_1.EndpointType)) {
13
+ return null;
14
+ }
15
+ const e = v.value;
16
+ return {
17
+ endpointUrl: e.endpointUrl ?? undefined,
18
+ securityMode: e.securityMode,
19
+ securityPolicyUri: e.securityPolicyUri ?? undefined,
20
+ transportProfileUri: e.transportProfileUri ?? undefined
21
+ };
22
+ }
23
+ /**
24
+ * Build a handler that, after the SecurityAdmin + encrypted-channel checks,
25
+ * applies `mutate` to the Role's restrictions, persists, and audits.
26
+ */
27
+ function makeRestrictionHandler(options, method, mutate) {
28
+ const { onMutation, onAudit } = options;
29
+ return async function (inputArguments, context) {
30
+ const insecure = (0, security_checks_js_1.checkEncryptedChannel)(context);
31
+ if (insecure)
32
+ return insecure;
33
+ const denied = (0, security_checks_js_1.checkSecurityAdminAccess)(context);
34
+ if (denied)
35
+ return denied;
36
+ const roleNode = this.parent;
37
+ if (!roleNode) {
38
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError };
39
+ }
40
+ const statusCode = mutate(roleNode.nodeId, inputArguments);
41
+ if (statusCode === null) {
42
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
43
+ }
44
+ if (statusCode === node_opcua_status_code_1.StatusCodes.Good && onMutation) {
45
+ await onMutation();
46
+ }
47
+ onAudit?.({
48
+ method,
49
+ roleNodeId: roleNode.nodeId,
50
+ methodNodeId: this.nodeId,
51
+ userName: context.getUserName(),
52
+ inputArguments,
53
+ statusCode
54
+ });
55
+ return { statusCode };
56
+ };
57
+ }
58
+ /** AddApplication (§4.4.7). */
59
+ function makeAddApplicationHandler(options) {
60
+ return makeRestrictionHandler(options, "AddApplication", (roleNodeId, args) => {
61
+ const uri = (0, variant_args_js_1.asString)(args[0]);
62
+ if (uri === null)
63
+ return null;
64
+ return options.restrictionStore.addApplication(roleNodeId, uri) ? node_opcua_status_code_1.StatusCodes.Good : node_opcua_status_code_1.StatusCodes.BadAlreadyExists;
65
+ });
66
+ }
67
+ /** RemoveApplication (§4.4.8). */
68
+ function makeRemoveApplicationHandler(options) {
69
+ return makeRestrictionHandler(options, "RemoveApplication", (roleNodeId, args) => {
70
+ const uri = (0, variant_args_js_1.asString)(args[0]);
71
+ if (uri === null)
72
+ return null;
73
+ return options.restrictionStore.removeApplication(roleNodeId, uri) ? node_opcua_status_code_1.StatusCodes.Good : node_opcua_status_code_1.StatusCodes.BadNotFound;
74
+ });
75
+ }
76
+ /** AddEndpoint (§4.4.9). */
77
+ function makeAddEndpointHandler(options) {
78
+ return makeRestrictionHandler(options, "AddEndpoint", (roleNodeId, args) => {
79
+ const endpoint = endpointFromArg(args[0]);
80
+ if (endpoint === null)
81
+ return null;
82
+ return options.restrictionStore.addEndpoint(roleNodeId, endpoint) ? node_opcua_status_code_1.StatusCodes.Good : node_opcua_status_code_1.StatusCodes.BadAlreadyExists;
83
+ });
84
+ }
85
+ /** RemoveEndpoint (§4.4.10). */
86
+ function makeRemoveEndpointHandler(options) {
87
+ return makeRestrictionHandler(options, "RemoveEndpoint", (roleNodeId, args) => {
88
+ const endpoint = endpointFromArg(args[0]);
89
+ if (endpoint === null)
90
+ return null;
91
+ return options.restrictionStore.removeEndpoint(roleNodeId, endpoint) ? node_opcua_status_code_1.StatusCodes.Good : node_opcua_status_code_1.StatusCodes.BadNotFound;
92
+ });
93
+ }
94
+ //# sourceMappingURL=bind_restriction_methods.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bind_restriction_methods.js","sourceRoot":"","sources":["../source/bind_restriction_methods.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @module node-opcua-role-set-server
3
+ *
4
+ * Method handlers for RoleType AddIdentity / RemoveIdentity
5
+ * and RoleSetType AddRole / RemoveRole.
6
+ *
7
+ * Each handler follows the OPC UA method binding pattern:
8
+ * - `this` is the UAMethod node
9
+ * - `inputArguments` is Variant[]
10
+ * - `context` is ISessionContext
11
+ * - returns Promise<CallMethodResultOptions>
12
+ */
13
+ import type { ISessionContext, UAMethod } from "node-opcua-address-space-base";
14
+ import type { NodeId } from "node-opcua-nodeid";
15
+ import { type IIdentityMappingStore } from "node-opcua-role-set-common";
16
+ import type { CallMethodResultOptions } from "node-opcua-service-call";
17
+ import { type StatusCode } from "node-opcua-status-code";
18
+ import type { Variant } from "node-opcua-variant";
19
+ /**
20
+ * Details of an identity-mapping change, passed to {@link BindRoleMethodsOptions.onAudit}
21
+ * so the caller can raise a `RoleMappingRuleChangedAuditEventType` (OPC 10000-18 §4.5).
22
+ * Raised for every authorized attempt — both successful updates and refusals
23
+ * (e.g. immutable Role, duplicate) — with the resulting `statusCode`.
24
+ */
25
+ export interface RoleMappingRuleChangedAudit {
26
+ method: "AddIdentity" | "RemoveIdentity" | "AddApplication" | "RemoveApplication" | "AddEndpoint" | "RemoveEndpoint";
27
+ roleNodeId: NodeId;
28
+ methodNodeId: NodeId;
29
+ userName: string;
30
+ inputArguments: Variant[];
31
+ statusCode: StatusCode;
32
+ }
33
+ export interface BindRoleMethodsOptions {
34
+ store: IIdentityMappingStore;
35
+ /** Called after every mutation (add/remove) so the caller can persist. */
36
+ onMutation?: () => Promise<void>;
37
+ /** Called after an authorized AddIdentity/RemoveIdentity attempt to raise an audit event. */
38
+ onAudit?: (audit: RoleMappingRuleChangedAudit) => void;
39
+ }
40
+ /**
41
+ * Create an AddIdentity method handler bound to the given store.
42
+ * The handler extracts the role NodeId from its parent Role object.
43
+ */
44
+ export declare function makeAddIdentityHandler(options: BindRoleMethodsOptions): (this: UAMethod, inputArguments: Variant[], context: ISessionContext) => Promise<CallMethodResultOptions>;
45
+ /**
46
+ * Create a RemoveIdentity method handler bound to the given store.
47
+ */
48
+ export declare function makeRemoveIdentityHandler(options: BindRoleMethodsOptions): (this: UAMethod, inputArguments: Variant[], context: ISessionContext) => Promise<CallMethodResultOptions>;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeAddIdentityHandler = makeAddIdentityHandler;
4
+ exports.makeRemoveIdentityHandler = makeRemoveIdentityHandler;
5
+ const node_opcua_nodeid_1 = require("node-opcua-nodeid");
6
+ const node_opcua_role_set_common_1 = require("node-opcua-role-set-common");
7
+ const node_opcua_status_code_1 = require("node-opcua-status-code");
8
+ const node_opcua_types_1 = require("node-opcua-types");
9
+ const security_checks_js_1 = require("./security_checks.js");
10
+ /**
11
+ * Well-known Roles whose mapping rules a Server shall not allow to be
12
+ * changed (OPC 10000-18 §4.3): Anonymous, AuthenticatedUser and
13
+ * TrustedApplication. (TrustedApplication has no constant in this build and
14
+ * is matched by NodeId when present.)
15
+ */
16
+ function isImmutableRole(roleId) {
17
+ return (0, node_opcua_nodeid_1.sameNodeId)(roleId, node_opcua_role_set_common_1.WellKnownRoleIds.Anonymous) || (0, node_opcua_nodeid_1.sameNodeId)(roleId, node_opcua_role_set_common_1.WellKnownRoleIds.AuthenticatedUser);
18
+ }
19
+ /**
20
+ * Administrative Roles to which weak (Anonymous / AuthenticatedUser) identity
21
+ * rules must not be added (OPC 10000-18 §4.4.5: a Server should refuse to add
22
+ * an ANONYMOUS_5 rule to Roles with administrator privileges).
23
+ */
24
+ function isPrivilegedRole(roleId) {
25
+ return (0, node_opcua_nodeid_1.sameNodeId)(roleId, node_opcua_role_set_common_1.WellKnownRoleIds.SecurityAdmin) || (0, node_opcua_nodeid_1.sameNodeId)(roleId, node_opcua_role_set_common_1.WellKnownRoleIds.ConfigureAdmin);
26
+ }
27
+ /** True for weak criteria that must not be granted to administrative Roles. */
28
+ function isWeakCriteria(criteriaType) {
29
+ return criteriaType === node_opcua_types_1.IdentityCriteriaType.Anonymous || criteriaType === node_opcua_types_1.IdentityCriteriaType.AuthenticatedUser;
30
+ }
31
+ /**
32
+ * Extract the IdentityMappingRuleType from the first input argument.
33
+ */
34
+ function extractIdentityRule(inputArguments) {
35
+ if (!inputArguments || inputArguments.length < 1) {
36
+ return null;
37
+ }
38
+ const value = inputArguments[0].value;
39
+ if (value instanceof node_opcua_types_1.IdentityMappingRuleType) {
40
+ return value;
41
+ }
42
+ return null;
43
+ }
44
+ /**
45
+ * Create an AddIdentity method handler bound to the given store.
46
+ * The handler extracts the role NodeId from its parent Role object.
47
+ */
48
+ function makeAddIdentityHandler(options) {
49
+ const { store, onMutation, onAudit } = options;
50
+ return async function _addIdentity(inputArguments, context) {
51
+ // 1. The SecureChannel must be encrypted
52
+ const insecure = (0, security_checks_js_1.checkEncryptedChannel)(context);
53
+ if (insecure)
54
+ return insecure;
55
+ // 2. Security check
56
+ const denied = (0, security_checks_js_1.checkSecurityAdminAccess)(context);
57
+ if (denied)
58
+ return denied;
59
+ // 3. Extract the rule from input
60
+ const rule = extractIdentityRule(inputArguments);
61
+ if (!rule) {
62
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
63
+ }
64
+ // 4. The parent of this method is the Role object
65
+ const roleNode = this.parent;
66
+ if (!roleNode) {
67
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError };
68
+ }
69
+ // 5-7. Compute the outcome of the authorized rule-change attempt
70
+ let statusCode;
71
+ if (isImmutableRole(roleNode.nodeId)) {
72
+ // well-known immutable Roles cannot be changed (§4.3)
73
+ statusCode = node_opcua_status_code_1.StatusCodes.BadRequestNotAllowed;
74
+ }
75
+ else if (isWeakCriteria(rule.criteriaType) && isPrivilegedRole(roleNode.nodeId)) {
76
+ // refuse weak (Anonymous/AuthenticatedUser) rules on administrative Roles (§4.4.5)
77
+ statusCode = node_opcua_status_code_1.StatusCodes.BadRequestNotAllowed;
78
+ }
79
+ else {
80
+ // duplicate rule is reported as Bad_AlreadyExists
81
+ statusCode = store.addIdentity(roleNode.nodeId, rule) ? node_opcua_status_code_1.StatusCodes.Good : node_opcua_status_code_1.StatusCodes.BadAlreadyExists;
82
+ }
83
+ if (statusCode === node_opcua_status_code_1.StatusCodes.Good && onMutation) {
84
+ await onMutation();
85
+ }
86
+ onAudit?.({
87
+ method: "AddIdentity",
88
+ roleNodeId: roleNode.nodeId,
89
+ methodNodeId: this.nodeId,
90
+ userName: context.getUserName(),
91
+ inputArguments,
92
+ statusCode
93
+ });
94
+ return { statusCode };
95
+ };
96
+ }
97
+ /**
98
+ * Create a RemoveIdentity method handler bound to the given store.
99
+ */
100
+ function makeRemoveIdentityHandler(options) {
101
+ const { store, onMutation, onAudit } = options;
102
+ return async function _removeIdentity(inputArguments, context) {
103
+ // 1. The SecureChannel must be encrypted
104
+ const insecure = (0, security_checks_js_1.checkEncryptedChannel)(context);
105
+ if (insecure)
106
+ return insecure;
107
+ // 2. Security check
108
+ const denied = (0, security_checks_js_1.checkSecurityAdminAccess)(context);
109
+ if (denied)
110
+ return denied;
111
+ // 3. Extract the rule from input
112
+ const rule = extractIdentityRule(inputArguments);
113
+ if (!rule) {
114
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
115
+ }
116
+ // 4. The parent of this method is the Role object
117
+ const roleNode = this.parent;
118
+ if (!roleNode) {
119
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInternalError };
120
+ }
121
+ // 5-6. Compute the outcome of the authorized rule-change attempt
122
+ let statusCode;
123
+ if (isImmutableRole(roleNode.nodeId)) {
124
+ statusCode = node_opcua_status_code_1.StatusCodes.BadRequestNotAllowed;
125
+ }
126
+ else {
127
+ statusCode = store.removeIdentity(roleNode.nodeId, rule) ? node_opcua_status_code_1.StatusCodes.Good : node_opcua_status_code_1.StatusCodes.BadNoMatch;
128
+ }
129
+ if (statusCode === node_opcua_status_code_1.StatusCodes.Good && onMutation) {
130
+ await onMutation();
131
+ }
132
+ onAudit?.({
133
+ method: "RemoveIdentity",
134
+ roleNodeId: roleNode.nodeId,
135
+ methodNodeId: this.nodeId,
136
+ userName: context.getUserName(),
137
+ inputArguments,
138
+ statusCode
139
+ });
140
+ return { statusCode };
141
+ };
142
+ }
143
+ //# sourceMappingURL=bind_role_methods.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bind_role_methods.js","sourceRoot":"","sources":["../source/bind_role_methods.ts"],"names":[],"mappings":"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"}