opcua_server/address_space/
utils.rs1use crate::node_manager::{ParsedReadValueId, ParsedWriteValue, RequestContext, ServerContext};
2use opcua_nodes::TypeTree;
3use opcua_types::{
4 AttributeId, DataEncoding, DataTypeId, DataValue, DateTime, NumericRange, StatusCode,
5 TimestampsToReturn, Variant, WriteMask,
6};
7use tracing::debug;
8
9use super::{AccessLevel, AddressSpace, HasNodeId, NodeType, Variable};
10
11pub fn is_readable(context: &RequestContext, node: &NodeType) -> Result<(), StatusCode> {
14 if !user_access_level(context, node).contains(AccessLevel::CURRENT_READ) {
15 Err(StatusCode::BadUserAccessDenied)
16 } else {
17 Ok(())
18 }
19}
20
21pub fn is_writable(
24 context: &RequestContext,
25 node: &NodeType,
26 attribute_id: AttributeId,
27) -> Result<(), StatusCode> {
28 if let (NodeType::Variable(_), AttributeId::Value) = (node, attribute_id) {
29 if !user_access_level(context, node).contains(AccessLevel::CURRENT_WRITE) {
30 return Err(StatusCode::BadUserAccessDenied);
31 }
32
33 Ok(())
34 } else {
35 let mask_value = match attribute_id {
36 AttributeId::BrowseName => WriteMask::BROWSE_NAME,
39 AttributeId::DisplayName => WriteMask::DISPLAY_NAME,
40 AttributeId::Description => WriteMask::DESCRIPTION,
41 AttributeId::WriteMask => WriteMask::WRITE_MASK,
42 AttributeId::UserWriteMask => WriteMask::USER_WRITE_MASK,
43 AttributeId::IsAbstract => WriteMask::IS_ABSTRACT,
44 AttributeId::Symmetric => WriteMask::SYMMETRIC,
45 AttributeId::InverseName => WriteMask::INVERSE_NAME,
46 AttributeId::ContainsNoLoops => WriteMask::CONTAINS_NO_LOOPS,
47 AttributeId::EventNotifier => WriteMask::EVENT_NOTIFIER,
48 AttributeId::Value => WriteMask::VALUE_FOR_VARIABLE_TYPE,
49 AttributeId::DataType => WriteMask::DATA_TYPE,
50 AttributeId::ValueRank => WriteMask::VALUE_RANK,
51 AttributeId::ArrayDimensions => WriteMask::ARRAY_DIMENSIONS,
52 AttributeId::AccessLevel => WriteMask::ACCESS_LEVEL,
53 AttributeId::UserAccessLevel => WriteMask::USER_ACCESS_LEVEL,
54 AttributeId::MinimumSamplingInterval => WriteMask::MINIMUM_SAMPLING_INTERVAL,
55 AttributeId::Historizing => WriteMask::HISTORIZING,
56 AttributeId::Executable => WriteMask::EXECUTABLE,
57 AttributeId::UserExecutable => WriteMask::USER_EXECUTABLE,
58 AttributeId::DataTypeDefinition => WriteMask::DATA_TYPE_DEFINITION,
59 AttributeId::RolePermissions => WriteMask::ROLE_PERMISSIONS,
60 AttributeId::AccessRestrictions => WriteMask::ACCESS_RESTRICTIONS,
61 AttributeId::AccessLevelEx => WriteMask::ACCESS_LEVEL_EX,
62 _ => return Err(StatusCode::BadNotWritable),
63 };
64
65 let write_mask = node.as_node().write_mask();
66 if write_mask.is_none() || write_mask.is_some_and(|wm| !wm.contains(mask_value)) {
67 return Err(StatusCode::BadNotWritable);
68 }
69 Ok(())
70 }
71}
72
73pub fn user_access_level(context: &RequestContext, node: &NodeType) -> AccessLevel {
75 let user_access_level = if let NodeType::Variable(ref node) = node {
76 node.user_access_level()
77 } else {
78 AccessLevel::CURRENT_READ
79 };
80 context.authenticator.effective_user_access_level(
81 &context.token,
82 user_access_level,
83 node.node_id(),
84 )
85}
86
87pub fn validate_node_read(
90 node: &NodeType,
91 context: &RequestContext,
92 node_to_read: &ParsedReadValueId,
93) -> Result<(), StatusCode> {
94 is_readable(context, node)?;
95
96 if node_to_read.attribute_id != AttributeId::Value
97 && node_to_read.index_range != NumericRange::None
98 {
99 return Err(StatusCode::BadIndexRangeDataMismatch);
100 }
101
102 if !is_supported_data_encoding(&node_to_read.data_encoding) {
103 debug!(
104 "read_node_value result for read node id {}, attribute {:?} is invalid data encoding",
105 node_to_read.node_id, node_to_read.attribute_id
106 );
107 return Err(StatusCode::BadDataEncodingInvalid);
108 }
109
110 Ok(())
111}
112
113pub fn validate_value_to_write(
116 variable: &Variable,
117 value: &Variant,
118 type_tree: &dyn TypeTree,
119) -> Result<(), StatusCode> {
120 let value_rank = variable.value_rank();
121 let node_data_type = variable.data_type();
122
123 if matches!(value, Variant::Empty) {
124 return Ok(());
125 }
126
127 if let Some(value_data_type) = value.data_type() {
128 let Some(data_type) = value_data_type.try_resolve(type_tree.namespaces()) else {
129 return Err(StatusCode::BadTypeMismatch);
130 };
131 let data_type_matches = type_tree.is_subtype_of(&data_type, &node_data_type);
133
134 if !data_type_matches {
135 if value.is_array() {
136 return Err(StatusCode::BadTypeMismatch);
137 }
138 match value {
142 Variant::ByteString(_) => {
143 if node_data_type == DataTypeId::Byte {
144 match value_rank {
145 -2 | -3 | 1 => Ok(()),
146 _ => Err(StatusCode::BadTypeMismatch),
147 }
148 } else {
149 Err(StatusCode::BadTypeMismatch)
150 }
151 }
152 _ => Ok(()),
153 }
154 } else {
155 Ok(())
156 }
157 } else {
158 Err(StatusCode::BadTypeMismatch)
159 }
160}
161
162pub fn validate_node_write(
165 node: &NodeType,
166 context: &RequestContext,
167 node_to_write: &ParsedWriteValue,
168 type_tree: &dyn TypeTree,
169) -> Result<(), StatusCode> {
170 is_writable(context, node, node_to_write.attribute_id)?;
171
172 if node_to_write.attribute_id != AttributeId::Value && node_to_write.index_range.has_range() {
173 return Err(StatusCode::BadWriteNotSupported);
174 }
175
176 let Some(value) = node_to_write.value.value.as_ref() else {
177 return Err(StatusCode::BadTypeMismatch);
178 };
179
180 if let (NodeType::Variable(var), AttributeId::Value) = (node, node_to_write.attribute_id) {
182 validate_value_to_write(var, value, type_tree)?;
183 }
184
185 Ok(())
186}
187
188pub fn is_supported_data_encoding(data_encoding: &DataEncoding) -> bool {
192 matches!(data_encoding, DataEncoding::Binary)
193}
194
195pub fn read_node_value(
199 node: &NodeType,
200 context: &RequestContext,
201 node_to_read: &ParsedReadValueId,
202 max_age: f64,
203 timestamps_to_return: TimestampsToReturn,
204) -> DataValue {
205 let mut result_value = DataValue::null();
206
207 let Some(attribute) = node.as_node().get_attribute_max_age(
208 timestamps_to_return,
209 node_to_read.attribute_id,
210 &node_to_read.index_range,
211 &node_to_read.data_encoding,
212 max_age,
213 ) else {
214 result_value.status = Some(StatusCode::BadAttributeIdInvalid);
215 return result_value;
216 };
217
218 let value = if node_to_read.attribute_id == AttributeId::UserAccessLevel {
219 match attribute.value {
220 Some(Variant::Byte(val)) => {
221 let access_level = AccessLevel::from_bits_truncate(val);
222 let access_level = context.authenticator.effective_user_access_level(
223 &context.token,
224 access_level,
225 node.node_id(),
226 );
227 Some(Variant::from(access_level.bits()))
228 }
229 Some(v) => Some(v),
230 _ => None,
231 }
232 } else {
233 attribute.value
234 };
235
236 let value = if node_to_read.attribute_id == AttributeId::UserExecutable {
237 match value {
238 Some(Variant::Boolean(val)) => Some(Variant::from(
239 val && context
240 .authenticator
241 .is_user_executable(&context.token, node.node_id()),
242 )),
243 r => r,
244 }
245 } else {
246 value
247 };
248
249 result_value.value = value;
250 result_value.status = attribute.status;
251 if matches!(node, NodeType::Variable(_)) && node_to_read.attribute_id == AttributeId::Value {
252 match timestamps_to_return {
253 TimestampsToReturn::Source => {
254 result_value.source_timestamp = attribute.source_timestamp;
255 result_value.source_picoseconds = attribute.source_picoseconds;
256 }
257 TimestampsToReturn::Server => {
258 result_value.server_timestamp = attribute.server_timestamp;
259 result_value.server_picoseconds = attribute.server_picoseconds;
260 }
261 TimestampsToReturn::Both => {
262 result_value.source_timestamp = attribute.source_timestamp;
263 result_value.source_picoseconds = attribute.source_picoseconds;
264 result_value.server_timestamp = attribute.server_timestamp;
265 result_value.server_picoseconds = attribute.server_picoseconds;
266 }
267 TimestampsToReturn::Neither | TimestampsToReturn::Invalid => {
268 }
270 }
271 }
272 result_value
273}
274
275pub fn write_node_value(
277 node: &mut NodeType,
278 node_to_write: &ParsedWriteValue,
279) -> Result<(), StatusCode> {
280 let now = DateTime::now();
281 if node_to_write.attribute_id == AttributeId::Value {
282 if let NodeType::Variable(variable) = node {
283 return variable.set_value_range(
284 node_to_write.value.value.clone().unwrap_or_default(),
285 &node_to_write.index_range,
286 node_to_write.value.status.unwrap_or_default(),
287 &now,
288 &node_to_write.value.source_timestamp.unwrap_or(now),
289 );
290 }
291 }
292 node.as_mut_node().set_attribute(
293 node_to_write.attribute_id,
294 node_to_write.value.value.clone().unwrap_or_default(),
295 )
296}
297
298pub fn add_namespaces(
301 context: &ServerContext,
302 address_space: &mut AddressSpace,
303 namespaces: &[&str],
304) -> Vec<u16> {
305 let mut type_tree = context.type_tree.write();
306 let mut res = Vec::new();
307 for ns in namespaces {
308 let idx = type_tree.namespaces_mut().add_namespace(ns);
309 address_space.add_namespace(ns, idx);
310 res.push(idx);
311 }
312 res
313}