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.
- package/LICENSE +22 -0
- package/README.md +188 -0
- package/dist/audit.d.ts +33 -0
- package/dist/audit.js +28 -0
- package/dist/audit.js.map +1 -0
- package/dist/bind_restriction_methods.d.ts +31 -0
- package/dist/bind_restriction_methods.js +94 -0
- package/dist/bind_restriction_methods.js.map +1 -0
- package/dist/bind_role_methods.d.ts +48 -0
- package/dist/bind_role_methods.js +143 -0
- package/dist/bind_role_methods.js.map +1 -0
- package/dist/bind_user_management.d.ts +58 -0
- package/dist/bind_user_management.js +153 -0
- package/dist/bind_user_management.js.map +1 -0
- package/dist/harden.d.ts +35 -0
- package/dist/harden.js +28 -0
- package/dist/harden.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/install_role_based_security.d.ts +78 -0
- package/dist/install_role_based_security.js +41 -0
- package/dist/install_role_based_security.js.map +1 -0
- package/dist/install_role_set.d.ts +73 -0
- package/dist/install_role_set.js +308 -0
- package/dist/install_role_set.js.map +1 -0
- package/dist/install_user_management.d.ts +68 -0
- package/dist/install_user_management.js +158 -0
- package/dist/install_user_management.js.map +1 -0
- package/dist/role_set_resolver.d.ts +23 -0
- package/dist/role_set_resolver.js +30 -0
- package/dist/role_set_resolver.js.map +1 -0
- package/dist/security_checks.d.ts +25 -0
- package/dist/security_checks.js +41 -0
- package/dist/security_checks.js.map +1 -0
- package/dist/user_management_user_manager.d.ts +37 -0
- package/dist/user_management_user_manager.js +72 -0
- package/dist/user_management_user_manager.js.map +1 -0
- package/dist/variant_args.d.ts +13 -0
- package/dist/variant_args.js +18 -0
- package/dist/variant_args.js.map +1 -0
- package/package.json +54 -0
- package/source/audit.ts +53 -0
- package/source/bind_restriction_methods.ts +117 -0
- package/source/bind_role_methods.ts +196 -0
- package/source/bind_user_management.ts +215 -0
- package/source/harden.ts +46 -0
- package/source/index.ts +48 -0
- package/source/install_role_based_security.ts +130 -0
- package/source/install_role_set.ts +426 -0
- package/source/install_user_management.ts +250 -0
- package/source/role_set_resolver.ts +38 -0
- package/source/security_checks.ts +47 -0
- package/source/user_management_user_manager.ts +104 -0
- 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
|
package/dist/audit.d.ts
ADDED
|
@@ -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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"}
|