Skip to main content

opcua_server/node_manager/
context.rs

1use std::{ops::Deref, sync::Arc};
2
3use crate::{
4    authenticator::{AuthManager, UserToken},
5    info::ServerInfo,
6    session::instance::Session,
7    SubscriptionCache,
8};
9use opcua_core::{sync::RwLock, trace_read_lock};
10use opcua_nodes::TypeTree;
11use opcua_types::{BrowseDescriptionResultMask, NodeId};
12use parking_lot::lock_api::{RawRwLock, RwLockReadGuard};
13use tracing::debug_span;
14use tracing_futures::Instrument;
15
16use super::{
17    view::{ExternalReferenceRequest, NodeMetadata},
18    DefaultTypeTree, NodeManagers,
19};
20
21/// Trait for providing a static reference to the type tree for a specific user.
22/// This allows subscriptions to hold a reference to the type tree.
23pub trait TypeTreeForUserStatic: Send + Sync {
24    /// Get the type tree read context. This may lock.
25    fn get_type_tree<'a>(&'a self) -> Box<dyn TypeTreeReadContext + 'a>;
26}
27
28impl<T> TypeTreeForUserStatic for RwLock<T>
29where
30    T: TypeTree + Send + Sync + 'static,
31{
32    fn get_type_tree<'a>(&'a self) -> Box<dyn TypeTreeReadContext + 'a> {
33        Box::new(trace_read_lock!(self))
34    }
35}
36
37/// Trait for providing a dynamic type tree for a user.
38/// This is a bit complex, it doesn't return a type tree directly,
39/// instead it returns something that wraps a type tree, for example
40/// a `RwLockReadGuard<'_, RawRwLock, dyn TypeTree>`
41pub trait TypeTreeForUser: Send + Sync {
42    /// Get the type tree for the user associated with the given `ctx`.
43    /// This can be the server global type tree, or a custom type tree for each individual user.
44    ///
45    /// It is sync, so you should do any setup in your [`AuthManager`] implementation.
46    fn get_type_tree_for_user<'a>(
47        &'a self,
48        ctx: &'a RequestContext,
49    ) -> Box<dyn TypeTreeReadContext + 'a>;
50
51    /// Get a static reference to a type tree getter for the current user.
52    /// This is used to allow subscriptions to hold a reference
53    /// to the type tree for events.
54    fn get_type_tree_static(&self, ctx: &RequestContext) -> Arc<dyn TypeTreeForUserStatic>;
55}
56
57pub(crate) struct DefaultTypeTreeGetter;
58
59impl TypeTreeForUser for DefaultTypeTreeGetter {
60    fn get_type_tree_for_user<'a>(
61        &'a self,
62        ctx: &'a RequestContext,
63    ) -> Box<dyn TypeTreeReadContext + 'a> {
64        Box::new(trace_read_lock!(ctx.type_tree))
65    }
66
67    fn get_type_tree_static(&self, ctx: &RequestContext) -> Arc<dyn TypeTreeForUserStatic> {
68        ctx.type_tree.clone()
69    }
70}
71
72/// Type returned from [`TypeTreeForUser`], a trait for something that dereferences
73/// to a `dyn TypeTree`.
74pub trait TypeTreeReadContext {
75    /// Dereference to a dynamic [TypeTree].
76    fn get(&self) -> &dyn TypeTree;
77}
78
79impl<R: RawRwLock, T: TypeTree> TypeTreeReadContext for RwLockReadGuard<'_, R, T> {
80    fn get(&self) -> &dyn TypeTree {
81        &**self
82    }
83}
84
85#[derive(Clone)]
86/// Context object passed during requests, contains useful context the node
87/// managers can use to execute service calls.
88pub struct RequestContext {
89    /// Index of the current node manager.
90    pub current_node_manager_index: usize,
91    /// Inner request context object, shared between service calls.
92    pub(crate) inner: Arc<RequestContextInner>,
93}
94
95// This isn't ideal, but the breaking change from having every field on
96// RequestContext be private is too big for now.
97impl Deref for RequestContext {
98    type Target = RequestContextInner;
99
100    fn deref(&self) -> &RequestContextInner {
101        &self.inner
102    }
103}
104
105/// Inner request context object, shared between service calls.
106pub struct RequestContextInner {
107    /// The full session object for the session responsible for this service call.
108    pub session: Arc<RwLock<Session>>,
109    /// The session ID for the session responsible for this service call.
110    pub session_id: u32,
111    /// The global `AuthManager` object.
112    pub authenticator: Arc<dyn AuthManager>,
113    /// The current user token.
114    pub token: UserToken,
115    /// Global type tree object.
116    pub type_tree: Arc<RwLock<DefaultTypeTree>>,
117    /// Wrapper to get a type tree
118    pub type_tree_getter: Arc<dyn TypeTreeForUser>,
119    /// Subscription cache, containing all subscriptions on the server.
120    pub subscriptions: Arc<SubscriptionCache>,
121    /// Server info object, containing configuration and other shared server
122    /// state.
123    pub info: Arc<ServerInfo>,
124}
125
126impl RequestContext {
127    /// Get the type tree for the current user.
128    pub fn get_type_tree_for_user<'a>(&'a self) -> Box<dyn TypeTreeReadContext + 'a> {
129        self.type_tree_getter.get_type_tree_for_user(self)
130    }
131
132    /// Get the session object responsible for this service call.
133    pub fn session(&self) -> &RwLock<Session> {
134        &self.session
135    }
136
137    /// Get the session ID for the session responsible for this service call.
138    pub fn session_id(&self) -> u32 {
139        self.session_id
140    }
141
142    /// Get the global `AuthManager` object.
143    pub fn authenticator(&self) -> &dyn AuthManager {
144        self.authenticator.as_ref()
145    }
146
147    /// Get the current user token.
148    pub fn user_token(&self) -> &UserToken {
149        &self.token
150    }
151
152    /// Get the global type tree object. If your server needs per-user type trees,
153    /// use `get_type_tree_for_user` instead.
154    pub fn type_tree(&self) -> &RwLock<DefaultTypeTree> {
155        &self.type_tree
156    }
157
158    /// Get the subscription cache.
159    pub fn subscriptions(&self) -> &SubscriptionCache {
160        &self.subscriptions
161    }
162
163    /// Get the server info object.
164    pub fn info(&self) -> &ServerInfo {
165        &self.info
166    }
167}
168
169/// Resolve a list of references.
170pub(crate) async fn resolve_external_references(
171    context: &RequestContext,
172    node_managers: &NodeManagers,
173    references: &[(&NodeId, BrowseDescriptionResultMask)],
174) -> Vec<Option<NodeMetadata>> {
175    let mut res: Vec<_> = references
176        .iter()
177        .map(|(n, mask)| ExternalReferenceRequest::new(n, *mask))
178        .collect();
179
180    for nm in node_managers.iter() {
181        let mut items: Vec<_> = res
182            .iter_mut()
183            .filter(|r| nm.owns_node(r.node_id()))
184            .collect();
185
186        nm.resolve_external_references(context, &mut items)
187            .instrument(debug_span!("resolve external references", node_manager = %nm.name()))
188            .await;
189    }
190
191    res.into_iter().map(|r| r.into_inner()).collect()
192}