/** * @module node-opcua-address-space */ /** biome-ignore-all lint/style/useLiteralEnumMembers: still needed */ import type { BaseNode, IAddressSpace, ISessionBase, ISessionContext, UAObject, UAObjectType } from "node-opcua-address-space-base"; import { ObjectIds } from "node-opcua-constants"; import { type Certificate } from "node-opcua-crypto/web"; import { AccessRestrictionsFlag, PermissionFlag, type QualifiedNameLike } from "node-opcua-data-model"; import type { PreciseClock } from "node-opcua-date-time"; import { NodeId, type NodeIdLike } from "node-opcua-nodeid"; import { AnonymousIdentityToken, MessageSecurityMode, PermissionType, type RolePermissionType, UserNameIdentityToken, X509IdentityToken } from "node-opcua-types"; export type { RolePermissionTypeOptions } from "node-opcua-types"; export { PermissionType, RolePermissionType } from "node-opcua-types"; export type AnyUserIdentityToken = UserNameIdentityToken | AnonymousIdentityToken | X509IdentityToken; import { WellKnownRoles } from "node-opcua-constants"; export { WellKnownRoles } from "node-opcua-constants"; /** @deprecated Use WellKnownRoles instead */ export declare const WellKnownRolesNodeId: { readonly Anonymous: ObjectIds.WellKnownRole_Anonymous; readonly AuthenticatedUser: ObjectIds.WellKnownRole_AuthenticatedUser; readonly ConfigureAdmin: ObjectIds.WellKnownRole_ConfigureAdmin; readonly Engineer: ObjectIds.WellKnownRole_Engineer; readonly Observer: ObjectIds.WellKnownRole_Observer; readonly Operator: ObjectIds.WellKnownRole_Operator; readonly SecurityAdmin: ObjectIds.WellKnownRole_SecurityAdmin; readonly Supervisor: ObjectIds.WellKnownRole_Supervisor; }; /** * OPC Unified Architecture, Part 3 13 Release 1.04 * 4.8.2 Well Known Roles * All Servers should support the well-known Roles which are defined in Table 2. The NodeIds * for the well-known Roles are defined in Part 6. * Table 2 – Well-Known Roles * BrowseName Suggested Permissions * * Anonymous The Role has very limited access for use when a Session has anonymous credentials. * AuthenticatedUser The Role has limited access for use when a Session has valid non-anonymous credentials * but has not been explicitly granted access to a Role. * Observer The Role is allowed to browse, read live data, read historical data/events or subscribe to data/events. * Operator The Role is allowed to browse, read live data, read historical data/events or subscribe to data/events. * In addition, the Session is allowed to write some live data and call some Methods. * Engineer The Role is allowed to browse, read/write configuration data, read historical data/events, * call Methods or subscribe to data/events. * Supervisor The Role is allowed to browse, read live data, read historical data/events, call Methods or * subscribe to data/events. * ConfigureAdmin The Role is allowed to change the non-security related config * SecurityAdmin The Role is allowed to change security related settings. */ export type WellKnownRolesSemiColumnSeparated = string; export interface IUserManager { /** * retrieve the roles of the given user * @returns semicolon separated list of roles */ getUserRoles?: (user: string) => NodeId[]; } /** * A temporary override for role resolution. * * When set on the server, `getUserRoles` is called * **before** the default `userManager`. Returning * a `NodeId[]` overrides the roles; returning `null` * falls through to the default resolution. */ export interface IRolePolicyOverride { getUserRoles(username: string): NodeId[] | null; } /** * Session attributes a resolver may use to evaluate application/endpoint * restrictions on a Role (OPC 10000-18 §4.4.1). */ export interface IRoleResolutionContext { /** ApplicationUri from the Client certificate, if any. */ applicationUri?: string | null; /** SecureChannel security mode. */ securityMode?: MessageSecurityMode; /** SecureChannel security policy URI. */ securityPolicyUri?: string; /** Endpoint URL used by the Session, if known. */ endpointUrl?: string; } /** * Pluggable role resolver (OPC 10000-18 §4.4). * * Receives the full UserIdentityToken so implementations can match by * Thumbprint, X509Subject, UserName, etc., plus an optional resolution context * to enforce application/endpoint restrictions. Registered on * IServerBase.roleResolvers by packages like node-opcua-role-set-server. */ export interface IRoleResolver { resolveRoles(userIdentityToken: AnyUserIdentityToken, context?: IRoleResolutionContext): NodeId[]; } /** * What to do when a permission cannot be resolved for a Session — either because no Role * could be attached to its identity, or because neither the node nor its namespace declares * any RolePermissions. * * - `"allow"` : grant every permission. This is what node-opcua has always done, and what * the vast majority of address spaces need, since almost no server declares * RolePermissions on its own nodes. * - `"deny"` : grant nothing. Fail-closed, for products that drive access entirely from * declared policy. Expect to set DefaultRolePermissions on every namespace, * otherwise the address space becomes unreadable. * * Note that this governs *Sessions only*. A SessionContext with no Session at all is an * in-process caller (SessionContext.defaultContext, PseudoSession) and stays permissive * whatever this is set to — see {@link SessionContext.getPermissions}. */ export type UnresolvedPermissionPolicy = "allow" | "deny"; export interface IServerBase { userManager?: IUserManager; rolePolicyOverride?: IRolePolicyOverride | null; /** Additional role resolvers (identity stores, LDAP, etc.) */ roleResolvers?: IRoleResolver[]; /** * how to treat a permission that could not be resolved for a Session. * @default "allow" */ unresolvedPermissionPolicy?: UnresolvedPermissionPolicy; } export interface SessionContextOptions { session?: ISessionBase; object?: UAObject | UAObjectType; server?: IServerBase; } /** * A Role, designated either by its NodeId or by its BrowseName. * * Roles outside namespace 0 (the GDS Roles, or any Role added with AddRole) have * NodeIds that depend on the order in which nodesets were loaded, so naming them is * more robust than hard coding `ns=1;i=1661`. Resolving a BrowseName requires an * address space, since the RoleSet is where the answer lives. * * A QualifiedName whose namespaceIndex is left undefined matches in any namespace, * as long as the BrowseName is unambiguous. */ export type RoleIdLike = NodeIdLike | QualifiedNameLike; /** * build the list of Role NodeIds a user is granted, from any of the accepted spellings: * * ```ts * makeRoles(WellKnownRoles.Observer); // a well known Role * makeRoles("Observer;Operator"); // semicolon separated names * makeRoles("ns=1;i=1661"); // an explicit NodeId * makeRoles(["Observer", "ns=1;i=1661"]); // mixed * makeRoles("1:DiscoveryAdmin", addressSpace); // BrowseName, namespace index * makeRoles("DiscoveryAdmin", addressSpace); // BrowseName, any namespace * makeRoles([{ name: "DiscoveryAdmin" }], addressSpace); // idem, as a QualifiedName * // when the namespace is known by its URI rather than by its index: * makeRoles([{ namespaceIndex: addressSpace.getNamespaceIndex(gdsUri), name: "DiscoveryAdmin" }], addressSpace); * ``` * * @param roleIds the Roles to resolve * @param addressSpace required only to resolve a Role by BrowseName, since that * lookup goes through the RoleSet */ export declare function makeRoles(roleIds: RoleIdLike[] | string | WellKnownRoles, addressSpace?: IAddressSpace): NodeId[]; export declare class SessionContext implements ISessionContext { static defaultContext: SessionContext; object: UAObject | UAObjectType | undefined; currentTime?: PreciseClock; continuationPoints: Buffer[]; readonly session?: ISessionBase; readonly server?: IServerBase; constructor(options?: SessionContextOptions); /** * The client's application-instance certificate, * or `null` if no secure channel is available. */ get clientCertificate(): Certificate | null; /** * The application URI extracted from the client * certificate's SubjectAltName, or `null` if * no certificate is available. */ get clientApplicationUri(): string | null; /** The URL of the Endpoint the Session was created on, or `null` if unknown. */ get endpointUrl(): string | null; toJSON(): Record; toString(): string; getUserName(): string; /** * getCurrentUserRoles * * guest => anonymous user (unauthenticated) * default => default authenticated user * */ getCurrentUserRoles(): NodeId[]; getApplicableRolePermissions(node: BaseNode): RolePermissionType[] | null; /** * true when this context carries no Session at all. * * That is an in-process caller — SessionContext.defaultContext, or a PseudoSession * driving the address space from inside the server — not a remote one. Such a caller * has already passed whatever authorization its own entry point applies, and the * address space is not the place to second-guess it, so it is always granted every * permission. This is deliberate, and distinct from a remote Session whose identity * merely failed to resolve to a Role: that case is governed by * IServerBase.unresolvedPermissionPolicy. */ private get isInProcessCaller(); /** what an unresolved permission means for this context: everything, or nothing */ private get unresolvedPermissions(); /** * the Roles to evaluate permissions against: the ones the user resolved to, plus the * Anonymous Role, which every Session stands on. * * Opc.Ua.NodeSet2.xml names exactly five Roles across its 854 RolePermission entries — * Anonymous, SecurityAdmin, ConfigureAdmin and the two SecurityKeyServer ones — and * never AuthenticatedUser, Observer, Operator, Engineer or Supervisor. Its 195 Anonymous * entries (Browse, Browse|Read, Browse|Call) are therefore not a privilege reserved for * unauthenticated Sessions; they are the floor granted to everyone. Read literally, an * authenticated user would be unable to browse the Server Object at all, and would have * strictly less access than an anonymous one. * * Adding the Anonymous Role here can only widen a permission set, never narrow it, and * only ever to what an unauthenticated Session already has — so it cannot grant anything * an attacker could not obtain by simply not authenticating. It leaves * getCurrentUserRoles() alone: the identity a Session reports stays truthful, only the * permission evaluation gains the baseline. */ private getRolesForPermissionEvaluation; getPermissions(node: BaseNode): PermissionFlag; getAccessRestrictions(node: BaseNode): AccessRestrictionsFlag; /** * * @param node * @returns true if the browse is denied (access is restricted) */ isBrowseAccessRestricted(node: BaseNode): boolean; /** * * @param node * @returns true if the context is access restricted */ isAccessRestricted(node: BaseNode): boolean; /** */ checkPermission(node: BaseNode, requestedPermission: PermissionType): boolean; currentUserHasRole(role: NodeIdLike): boolean; }