1use std::{self, fmt};
2
3use crate::{
4 attribute::AttributeId,
5 byte_string::ByteString,
6 constants,
7 data_value::DataValue,
8 extension_object::ExtensionObject,
9 localized_text::LocalizedText,
10 node_id::NodeId,
11 node_ids::{DataTypeId, ObjectId},
12 profiles,
13 qualified_name::QualifiedName,
14 request_header::RequestHeader,
15 response_header::ResponseHeader,
16 service_types::{
17 AnonymousIdentityToken, ApplicationDescription, ApplicationType, Argument, CallMethodRequest,
18 DataChangeFilter, DataChangeTrigger, EndpointDescription, enums::DeadbandType, MessageSecurityMode, MonitoredItemCreateRequest, MonitoringMode,
19 MonitoringParameters, ReadValueId, ServerDiagnosticsSummaryDataType, ServiceCounterDataType, ServiceFault,
20 SignatureData, UserNameIdentityToken, UserTokenPolicy, UserTokenType,
21 },
22 status_codes::StatusCode,
23 string::UAString,
24 variant::Variant,
25};
26
27pub trait MessageInfo {
29 fn object_id(&self) -> ObjectId;
31}
32
33impl ServiceFault {
34 pub fn new(request_header: &RequestHeader, service_result: StatusCode) -> ServiceFault {
35 ServiceFault {
36 response_header: ResponseHeader::new_service_result(request_header, service_result)
37 }
38 }
39}
40
41impl UserTokenPolicy {
42 pub fn anonymous() -> UserTokenPolicy {
43 UserTokenPolicy {
44 policy_id: UAString::from("anonymous"),
45 token_type: UserTokenType::Anonymous,
46 issued_token_type: UAString::null(),
47 issuer_endpoint_url: UAString::null(),
48 security_policy_uri: UAString::null(),
49 }
50 }
51}
52
53impl DataChangeFilter {
54 pub fn compare(&self, v1: &DataValue, v2: &DataValue, eu_range: Option<(f64, f64)>) -> bool {
57 match self.trigger {
58 DataChangeTrigger::Status => {
59 v1.status == v2.status
60 }
61 DataChangeTrigger::StatusValue => {
62 v1.status == v2.status &&
63 self.compare_value_option(&v1.value, &v2.value, eu_range)
64 }
65 DataChangeTrigger::StatusValueTimestamp => {
66 v1.status == v2.status &&
67 self.compare_value_option(&v1.value, &v2.value, eu_range) &&
68 v1.server_timestamp == v2.server_timestamp
69 }
70 }
71 }
72
73 pub fn compare_value_option(&self, v1: &Option<Variant>, v2: &Option<Variant>, eu_range: Option<(f64, f64)>) -> bool {
75 match (v1, v2) {
76 (Some(_), None) | (None, Some(_)) => {
77 false
78 }
79 (None, None) => {
80 true
82 }
83 (Some(v1), Some(v2)) => {
84 self.compare_value(v1, v2, eu_range).unwrap_or(true)
86 }
87 }
88 }
89
90 pub fn compare_value(&self, v1: &Variant, v2: &Variant, eu_range: Option<(f64, f64)>) -> std::result::Result<bool, StatusCode> {
103 if self.deadband_type == DeadbandType::None as u32 {
105 Ok(v1 == v2)
107 } else {
108 match (v1.as_f64(), v2.as_f64()) {
110 (None, _) | (_, None) => Ok(false),
111 (Some(v1), Some(v2)) => {
112 if self.deadband_value < 0f64 {
113 Err(StatusCode::BadDeadbandFilterInvalid)
114 } else if self.deadband_type == DeadbandType::Absolute as u32 {
115 Ok(DataChangeFilter::abs_compare(v1, v2, self.deadband_value))
116 } else if self.deadband_type == DeadbandType::Percent as u32 {
117 match eu_range {
118 None => Err(StatusCode::BadDeadbandFilterInvalid),
119 Some((low, high)) => {
120 if low >= high {
121 Err(StatusCode::BadDeadbandFilterInvalid)
122 } else {
123 Ok(DataChangeFilter::pct_compare(v1, v2, low, high, self.deadband_value))
124 }
125 }
126 }
127 } else {
128 Err(StatusCode::BadDeadbandFilterInvalid)
130 }
131 }
132 }
133 }
134 }
135
136 pub fn abs_compare(v1: f64, v2: f64, threshold_diff: f64) -> bool {
139 let diff = (v1 - v2).abs();
140 diff <= threshold_diff
141 }
142
143 pub fn pct_compare(v1: f64, v2: f64, low: f64, high: f64, threshold_pct_change: f64) -> bool {
147 let v1_pct = 100f64 * (v1 - low) / (high - low);
148 let v2_pct = 100f64 * (v2 - low) / (high - low);
149 let pct_change = (v1_pct - v2_pct).abs();
150 pct_change <= threshold_pct_change
152 }
153}
154
155impl EndpointDescription {
156 pub fn find_policy(&self, token_type: UserTokenType) -> Option<&UserTokenPolicy> {
158 if let Some(ref policies) = self.user_identity_tokens {
159 policies.iter().find(|t| t.token_type == token_type)
160 } else {
161 None
162 }
163 }
164
165 pub fn find_policy_by_id(&self, policy_id: &str) -> Option<&UserTokenPolicy> {
167 if let Some(ref policies) = self.user_identity_tokens {
168 policies.iter().find(|t| t.policy_id.as_ref() == policy_id)
169 } else {
170 None
171 }
172 }
173}
174
175impl UserNameIdentityToken {
176 pub fn is_valid(&self) -> bool {
178 !self.user_name.is_null() && !self.password.is_null()
179 }
180
181 pub fn plaintext_password(&self) -> Result<String, StatusCode> {
183 if !self.encryption_algorithm.is_empty() {
184 panic!();
186 }
187 String::from_utf8(self.password.as_ref().to_vec()).map_err(|_| StatusCode::BadDecodingError)
188 }
189
190 pub fn authenticate(&self, username: &str, password: &[u8]) -> Result<(), StatusCode> {
192 let valid = if self.is_valid() {
195 if self.encryption_algorithm.is_null() {
197 let id_user = self.user_name.as_ref();
199 let id_pass = self.password.value.as_ref().unwrap();
200 if username == id_user {
201 if password == id_pass.as_slice() {
202 true
203 } else {
204 error!("Authentication error: User name {} supplied by client is recognised but password is not", username);
205 false
206 }
207 } else {
208 error!("Authentication error: User name supplied by client is unrecognised");
209 false
210 }
211 } else {
212 error!("Authentication error: Unsupported encryption algorithm {}", self.encryption_algorithm.as_ref());
216 false
217 }
218 } else {
219 error!("Authentication error: User / pass credentials not supplied in token");
220 false
221 };
222 if valid {
223 Ok(())
224 } else {
225 Err(StatusCode::BadIdentityTokenRejected)
226 }
227 }
228}
229
230impl<'a> From<&'a NodeId> for ReadValueId {
231 fn from(node_id: &'a NodeId) -> Self {
232 Self::from(node_id.clone())
233 }
234}
235
236impl From<NodeId> for ReadValueId {
237 fn from(node_id: NodeId) -> Self {
238 ReadValueId {
239 node_id,
240 attribute_id: AttributeId::Value as u32,
241 index_range: UAString::null(),
242 data_encoding: QualifiedName::null(),
243 }
244 }
245}
246
247impl<'a> From<(u16, &'a str)> for ReadValueId {
248 fn from(v: (u16, &'a str)) -> Self {
249 Self::from(NodeId::from(v))
250 }
251}
252
253impl Default for AnonymousIdentityToken {
254 fn default() -> Self {
255 AnonymousIdentityToken {
256 policy_id: UAString::from(profiles::SECURITY_USER_TOKEN_POLICY_ANONYMOUS)
257 }
258 }
259}
260
261impl SignatureData {
262 pub fn null() -> SignatureData {
263 SignatureData {
264 algorithm: UAString::null(),
265 signature: ByteString::null(),
266 }
267 }
268}
269
270impl Into<MonitoredItemCreateRequest> for NodeId {
271 fn into(self) -> MonitoredItemCreateRequest {
272 MonitoredItemCreateRequest::new(self.into(), MonitoringMode::Reporting, MonitoringParameters::default())
273 }
274}
275
276impl MonitoredItemCreateRequest {
277 pub fn new(item_to_monitor: ReadValueId, monitoring_mode: MonitoringMode, requested_parameters: MonitoringParameters) -> MonitoredItemCreateRequest {
279 MonitoredItemCreateRequest {
280 item_to_monitor,
281 monitoring_mode,
282 requested_parameters,
283 }
284 }
285}
286
287impl Default for ApplicationDescription {
288 fn default() -> Self {
289 Self {
290 application_uri: UAString::null(),
291 product_uri: UAString::null(),
292 application_name: LocalizedText::null(),
293 application_type: ApplicationType::Server,
294 gateway_server_uri: UAString::null(),
295 discovery_profile_uri: UAString::null(),
296 discovery_urls: None,
297 }
298 }
299}
300
301impl Default for MonitoringParameters {
302 fn default() -> Self {
303 MonitoringParameters {
304 client_handle: 0,
305 sampling_interval: -1f64,
306 filter: ExtensionObject::null(),
307 queue_size: 1,
308 discard_oldest: true,
309 }
310 }
311}
312
313impl Into<CallMethodRequest> for (NodeId, NodeId, Option<Vec<Variant>>) {
314 fn into(self) -> CallMethodRequest {
315 CallMethodRequest {
316 object_id: self.0,
317 method_id: self.1,
318 input_arguments: self.2,
319 }
320 }
321}
322
323impl Default for ServerDiagnosticsSummaryDataType {
324 fn default() -> Self {
325 ServerDiagnosticsSummaryDataType {
326 server_view_count: 0,
327 current_session_count: 0,
328 cumulated_session_count: 0,
329 security_rejected_session_count: 0,
330 rejected_session_count: 0,
331 session_timeout_count: 0,
332 session_abort_count: 0,
333 current_subscription_count: 0,
334 cumulated_subscription_count: 0,
335 publishing_interval_count: 0,
336 security_rejected_requests_count: 0,
337 rejected_requests_count: 0,
338 }
339 }
340}
341
342impl<'a> From<&'a str> for EndpointDescription {
343 fn from(v: &'a str) -> Self {
344 EndpointDescription::from((v, constants::SECURITY_POLICY_NONE_URI, MessageSecurityMode::None))
345 }
346}
347
348impl<'a> From<(&'a str, &'a str, MessageSecurityMode)> for EndpointDescription {
349 fn from(v: (&'a str, &'a str, MessageSecurityMode)) -> Self {
350 EndpointDescription::from((v.0, v.1, v.2, None))
351 }
352}
353
354impl<'a> From<(&'a str, &'a str, MessageSecurityMode, UserTokenPolicy)> for EndpointDescription {
355 fn from(v: (&'a str, &'a str, MessageSecurityMode, UserTokenPolicy)) -> Self {
356 EndpointDescription::from((v.0, v.1, v.2, Some(vec![v.3])))
357 }
358}
359
360impl<'a> From<(&'a str, &'a str, MessageSecurityMode, Vec<UserTokenPolicy>)> for EndpointDescription {
361 fn from(v: (&'a str, &'a str, MessageSecurityMode, Vec<UserTokenPolicy>)) -> Self {
362 EndpointDescription::from((v.0, v.1, v.2, Some(v.3)))
363 }
364}
365
366impl<'a> From<(&'a str, &'a str, MessageSecurityMode, Option<Vec<UserTokenPolicy>>)> for EndpointDescription {
367 fn from(v: (&'a str, &'a str, MessageSecurityMode, Option<Vec<UserTokenPolicy>>)) -> Self {
368 EndpointDescription {
369 endpoint_url: UAString::from(v.0),
370 security_policy_uri: UAString::from(v.1),
371 security_mode: v.2,
372 server: ApplicationDescription::default(),
373 security_level: 0,
374 server_certificate: ByteString::null(),
375 transport_profile_uri: UAString::null(),
376 user_identity_tokens: v.3,
377 }
378 }
379}
380
381const MESSAGE_SECURITY_MODE_NONE: &str = "None";
382const MESSAGE_SECURITY_MODE_SIGN: &str = "Sign";
383const MESSAGE_SECURITY_MODE_SIGN_AND_ENCRYPT: &str = "SignAndEncrypt";
384
385impl fmt::Display for MessageSecurityMode {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 let name = match self {
388 MessageSecurityMode::None => MESSAGE_SECURITY_MODE_NONE,
389 MessageSecurityMode::Sign => MESSAGE_SECURITY_MODE_SIGN,
390 MessageSecurityMode::SignAndEncrypt => MESSAGE_SECURITY_MODE_SIGN_AND_ENCRYPT,
391 _ => "",
392 };
393 write!(f, "{}", name)
394 }
395}
396
397impl From<MessageSecurityMode> for String {
398 fn from(security_mode: MessageSecurityMode) -> Self {
399 String::from(
400 match security_mode {
401 MessageSecurityMode::None => MESSAGE_SECURITY_MODE_NONE,
402 MessageSecurityMode::Sign => MESSAGE_SECURITY_MODE_SIGN,
403 MessageSecurityMode::SignAndEncrypt => MESSAGE_SECURITY_MODE_SIGN_AND_ENCRYPT,
404 _ => "",
405 }
406 )
407 }
408}
409
410impl<'a> From<&'a str> for MessageSecurityMode {
411 fn from(str: &'a str) -> Self {
412 match str {
413 MESSAGE_SECURITY_MODE_NONE => MessageSecurityMode::None,
414 MESSAGE_SECURITY_MODE_SIGN => MessageSecurityMode::Sign,
415 MESSAGE_SECURITY_MODE_SIGN_AND_ENCRYPT => MessageSecurityMode::SignAndEncrypt,
416 _ => {
417 error!("Specified security mode \"{}\" is not recognized", str);
418 MessageSecurityMode::Invalid
419 }
420 }
421 }
422}
423
424impl From<(&str, DataTypeId)> for Argument {
425 fn from(v: (&str, DataTypeId)) -> Self {
426 Argument {
427 name: UAString::from(v.0),
428 data_type: v.1.into(),
429 value_rank: -1,
430 array_dimensions: None,
431 description: LocalizedText::new("", ""),
432 }
433 }
434}
435
436impl Default for ServiceCounterDataType {
437 fn default() -> Self {
438 Self {
439 total_count: 0,
440 error_count: 0,
441 }
442 }
443}
444
445impl ServiceCounterDataType {
446 pub fn success(&mut self) {
447 self.total_count += 1;
448 }
449
450 pub fn error(&mut self) {
451 self.total_count += 1;
452 self.error_count += 1;
453 }
454}