1use std::{
8 collections::BTreeMap,
9 path::{Path, PathBuf},
10};
11
12use serde::{Deserialize, Serialize};
13use tracing::{trace, warn};
14
15use crate::constants;
16use opcua_core::{comms::url::url_matches_except_host, config::Config};
17use opcua_crypto::{CertificateStore, SecurityPolicy, Thumbprint};
18use opcua_types::{
19 ApplicationDescription, ApplicationType, DecodingOptions, LocalizedText, MessageSecurityMode,
20 UAString,
21};
22
23use super::{endpoint::ServerEndpoint, limits::Limits};
24
25pub const ANONYMOUS_USER_TOKEN_ID: &str = "ANONYMOUS";
27
28#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
29pub struct TcpConfig {
31 pub hello_timeout: u32,
33 pub host: String,
35 pub port: u16,
37}
38
39#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Default)]
40pub struct ServerUserToken {
42 pub user: String,
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub pass: Option<String>,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub x509: Option<String>,
50 #[serde(skip)]
52 pub thumbprint: Option<Thumbprint>,
53 #[serde(default)]
54 pub read_diagnostics: bool,
56}
57
58impl ServerUserToken {
59 pub fn user_pass<T>(user: T, pass: T) -> Self
61 where
62 T: Into<String>,
63 {
64 ServerUserToken {
65 user: user.into(),
66 pass: Some(pass.into()),
67 x509: None,
68 thumbprint: None,
69 read_diagnostics: false,
70 }
71 }
72
73 pub fn x509<T>(user: T, cert_path: &Path) -> Self
75 where
76 T: Into<String>,
77 {
78 ServerUserToken {
79 user: user.into(),
80 pass: None,
81 x509: Some(cert_path.to_string_lossy().to_string()),
82 thumbprint: None,
83 read_diagnostics: false,
84 }
85 }
86
87 pub fn read_thumbprint(&mut self) {
89 if self.is_x509() && self.thumbprint.is_none() {
90 if let Some(ref x509_path) = self.x509 {
93 let path = PathBuf::from(x509_path);
94 if let Ok(x509) = CertificateStore::read_cert(&path) {
95 self.thumbprint = Some(x509.thumbprint());
96 }
97 }
98 }
99 }
100
101 pub fn validate(&self, id: &str) -> Result<(), Vec<String>> {
104 let mut errors = Vec::new();
105 if id == ANONYMOUS_USER_TOKEN_ID {
106 errors.push(format!(
107 "User token {id} is invalid because id is a reserved value, use another value."
108 ));
109 }
110 if self.user.is_empty() {
111 errors.push(format!("User token {id} has an empty user name."));
112 }
113 if self.pass.is_some() && self.x509.is_some() {
114 errors.push(format!(
115 "User token {id} holds a password and certificate info - it cannot be both."
116 ));
117 } else if self.pass.is_none() && self.x509.is_none() {
118 errors.push(format!(
119 "User token {id} fails to provide a password or certificate info."
120 ));
121 }
122 if errors.is_empty() {
123 Ok(())
124 } else {
125 Err(errors)
126 }
127 }
128
129 pub fn is_user_pass(&self) -> bool {
131 self.x509.is_none()
132 }
133
134 pub fn is_x509(&self) -> bool {
136 self.x509.is_some()
137 }
138
139 pub fn read_diagnostics(mut self, read: bool) -> Self {
141 self.read_diagnostics = read;
142 self
143 }
144}
145
146#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
147pub struct CertificateValidation {
149 pub trust_client_certs: bool,
152 pub check_time: bool,
154}
155
156impl Default for CertificateValidation {
157 fn default() -> Self {
158 Self {
159 trust_client_certs: false,
160 check_time: true,
161 }
162 }
163}
164
165#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
166pub struct ServerConfig {
168 pub application_name: String,
170 pub application_uri: String,
172 pub product_uri: String,
174 #[serde(default)]
177 pub create_sample_keypair: bool,
178 #[serde(default)]
180 pub certificate_path: Option<PathBuf>,
181 #[serde(default)]
183 pub private_key_path: Option<PathBuf>,
184 #[serde(default)]
186 pub certificate_validation: CertificateValidation,
187 pub pki_dir: PathBuf,
189 #[serde(default)]
192 pub discovery_server_url: Option<String>,
193 pub tcp_config: TcpConfig,
195 #[serde(default)]
197 pub limits: Limits,
198 #[serde(default)]
200 pub locale_ids: Vec<String>,
201 pub user_tokens: BTreeMap<String, ServerUserToken>,
203 pub discovery_urls: Vec<String>,
205 #[serde(default)]
207 pub default_endpoint: Option<String>,
208 pub endpoints: BTreeMap<String, ServerEndpoint>,
210 #[serde(default = "defaults::subscription_poll_interval_ms")]
212 pub subscription_poll_interval_ms: u64,
213 #[serde(default = "defaults::publish_timeout_default_ms")]
215 pub publish_timeout_default_ms: u64,
216 #[serde(default = "defaults::max_timeout_ms")]
223 pub max_timeout_ms: u32,
224 #[serde(default = "defaults::max_secure_channel_token_lifetime_ms")]
229 pub max_secure_channel_token_lifetime_ms: u32,
230 #[serde(default = "defaults::max_session_timeout_ms")]
235 pub max_session_timeout_ms: u64,
236 #[serde(default)]
238 pub diagnostics: bool,
239 #[serde(default = "defaults::session_nonce_length")]
241 pub session_nonce_length: usize,
242 #[serde(default = "defaults::reverse_connect_failure_delay_ms")]
245 pub reverse_connect_failure_delay_ms: u64,
246}
247
248mod defaults {
249 use crate::constants;
250
251 pub(super) fn subscription_poll_interval_ms() -> u64 {
252 constants::SUBSCRIPTION_TIMER_RATE_MS
253 }
254
255 pub(super) fn publish_timeout_default_ms() -> u64 {
256 constants::DEFAULT_PUBLISH_TIMEOUT_MS
257 }
258
259 pub(super) fn max_timeout_ms() -> u32 {
260 300_000
261 }
262
263 pub(super) fn max_secure_channel_token_lifetime_ms() -> u32 {
264 300_000
265 }
266
267 pub(super) fn max_session_timeout_ms() -> u64 {
268 constants::MAX_SESSION_TIMEOUT
269 }
270
271 pub(super) fn session_nonce_length() -> usize {
272 32
273 }
274
275 pub(super) fn reverse_connect_failure_delay_ms() -> u64 {
276 30_000
277 }
278}
279
280impl Config for ServerConfig {
281 fn validate(&self) -> Result<(), Vec<String>> {
282 let mut errors = Vec::new();
283 if self.application_name.is_empty() {
284 warn!("No application was set");
285 }
286 if self.application_uri.is_empty() {
287 warn!("No application uri was set");
288 }
289 if self.product_uri.is_empty() {
290 warn!("No product uri was set");
291 }
292 if self.endpoints.is_empty() {
293 errors.push("Server configuration is invalid. It defines no endpoints".to_owned());
294 }
295 for (id, endpoint) in &self.endpoints {
296 if let Err(e) = endpoint.validate(id, &self.user_tokens) {
297 errors.push(format!(
298 "Endpoint {id} failed to validate: {}",
299 e.join(", ")
300 ));
301 }
302 }
303 if let Some(ref default_endpoint) = self.default_endpoint {
304 if !self.endpoints.contains_key(default_endpoint) {
305 errors.push(format!(
306 "Endpoints does not contain default endpoint {default_endpoint}"
307 ));
308 }
309 }
310 for (id, user_token) in &self.user_tokens {
311 if let Err(e) = user_token.validate(id) {
312 errors.push(format!(
313 "User token {id} failed to validate: {}",
314 e.join(", ")
315 ))
316 }
317 }
318 if self.limits.max_array_length == 0 {
319 errors.push("Server configuration is invalid. Max array length is invalid".to_owned());
320 }
321 if self.limits.max_string_length == 0 {
322 errors.push("Server configuration is invalid. Max string length is invalid".to_owned());
323 }
324 if self.limits.max_byte_string_length == 0 {
325 errors.push(
326 "Server configuration is invalid. Max byte string length is invalid".to_owned(),
327 );
328 }
329 if self.discovery_urls.is_empty() {
330 errors.push("Server configuration is invalid. Discovery urls not set".to_owned());
331 }
332
333 if errors.is_empty() {
334 Ok(())
335 } else {
336 Err(errors)
337 }
338 }
339
340 fn application_name(&self) -> UAString {
341 UAString::from(&self.application_name)
342 }
343
344 fn application_uri(&self) -> UAString {
345 UAString::from(&self.application_uri)
346 }
347
348 fn product_uri(&self) -> UAString {
349 UAString::from(&self.product_uri)
350 }
351
352 fn application_type(&self) -> ApplicationType {
353 ApplicationType::Server
354 }
355
356 fn discovery_urls(&self) -> Option<Vec<UAString>> {
357 let discovery_urls: Vec<UAString> =
358 self.discovery_urls.iter().map(UAString::from).collect();
359 Some(discovery_urls)
360 }
361
362 fn application_description(&self) -> ApplicationDescription {
363 ApplicationDescription {
364 application_uri: self.application_uri(),
365 application_name: LocalizedText::new("", self.application_name().as_ref()),
366 application_type: self.application_type(),
367 product_uri: self.product_uri(),
368 gateway_server_uri: UAString::null(),
369 discovery_profile_uri: UAString::null(),
370 discovery_urls: self.discovery_urls(),
371 }
372 }
373}
374
375impl Default for ServerConfig {
376 fn default() -> Self {
377 let mut pki_dir = std::env::current_dir().unwrap();
378 pki_dir.push(Self::PKI_DIR);
379
380 ServerConfig {
381 application_name: String::new(),
382 application_uri: String::new(),
383 product_uri: String::new(),
384 create_sample_keypair: false,
385 certificate_path: None,
386 private_key_path: None,
387 pki_dir,
388 certificate_validation: CertificateValidation::default(),
389 discovery_server_url: None,
390 tcp_config: TcpConfig {
391 host: "127.0.0.1".to_string(),
392 port: constants::DEFAULT_RUST_OPC_UA_SERVER_PORT,
393 hello_timeout: constants::DEFAULT_HELLO_TIMEOUT_SECONDS,
394 },
395 limits: Limits::default(),
396 user_tokens: BTreeMap::new(),
397 locale_ids: vec!["en".to_string()],
398 discovery_urls: Vec::new(),
399 default_endpoint: None,
400 endpoints: BTreeMap::new(),
401 subscription_poll_interval_ms: defaults::subscription_poll_interval_ms(),
402 publish_timeout_default_ms: defaults::publish_timeout_default_ms(),
403 max_timeout_ms: defaults::max_timeout_ms(),
404 max_secure_channel_token_lifetime_ms: defaults::max_secure_channel_token_lifetime_ms(),
405 max_session_timeout_ms: defaults::max_session_timeout_ms(),
406 diagnostics: false,
407 session_nonce_length: defaults::session_nonce_length(),
408 reverse_connect_failure_delay_ms: defaults::reverse_connect_failure_delay_ms(),
409 }
410 }
411}
412
413impl ServerConfig {
414 pub const PKI_DIR: &'static str = "pki";
416
417 pub fn new<T>(
420 application_name: T,
421 user_tokens: BTreeMap<String, ServerUserToken>,
422 endpoints: BTreeMap<String, ServerEndpoint>,
423 ) -> Self
424 where
425 T: Into<String>,
426 {
427 let host = "127.0.0.1".to_string();
428 let port = constants::DEFAULT_RUST_OPC_UA_SERVER_PORT;
429
430 let application_name = application_name.into();
431 let application_uri = format!("urn:{application_name}");
432 let product_uri = format!("urn:{application_name}");
433 let discovery_server_url = Some(constants::DEFAULT_DISCOVERY_SERVER_URL.to_string());
434 let discovery_urls = vec![format!("opc.tcp://{}:{}/", host, port)];
435 let locale_ids = vec!["en".to_string()];
436
437 let mut pki_dir = std::env::current_dir().unwrap();
438 pki_dir.push(Self::PKI_DIR);
439
440 ServerConfig {
441 application_name,
442 application_uri,
443 product_uri,
444 certificate_validation: CertificateValidation {
445 trust_client_certs: false,
446 check_time: true,
447 },
448 pki_dir,
449 discovery_server_url,
450 tcp_config: TcpConfig {
451 host,
452 port,
453 hello_timeout: constants::DEFAULT_HELLO_TIMEOUT_SECONDS,
454 },
455 locale_ids,
456 user_tokens,
457 discovery_urls,
458 endpoints,
459 ..Default::default()
460 }
461 }
462
463 pub fn decoding_options(&self) -> DecodingOptions {
465 DecodingOptions {
466 client_offset: chrono::Duration::zero(),
467 max_message_size: self.limits.max_message_size,
468 max_chunk_count: self.limits.max_chunk_count,
469 max_string_length: self.limits.max_string_length,
470 max_byte_string_length: self.limits.max_byte_string_length,
471 max_array_length: self.limits.max_array_length,
472 ..Default::default()
473 }
474 }
475
476 pub fn add_endpoint(&mut self, id: &str, endpoint: ServerEndpoint) {
478 self.endpoints.insert(id.to_string(), endpoint);
479 }
480
481 pub fn read_x509_thumbprints(&mut self) {
483 self.user_tokens
484 .iter_mut()
485 .for_each(|(_, token)| token.read_thumbprint());
486 }
487
488 pub fn default_endpoint(&self) -> Option<&ServerEndpoint> {
490 if let Some(ref default_endpoint) = self.default_endpoint {
491 self.endpoints.get(default_endpoint)
492 } else {
493 None
494 }
495 }
496
497 pub fn find_endpoint(
500 &self,
501 endpoint_url: &str,
502 base_endpoint_url: &str,
503 security_policy: SecurityPolicy,
504 security_mode: MessageSecurityMode,
505 ) -> Option<&ServerEndpoint> {
506 let endpoint = self.endpoints.iter().find(|&(_, e)| {
507 if url_matches_except_host(&e.endpoint_url(base_endpoint_url), endpoint_url) {
509 if e.security_policy() == security_policy
510 && e.message_security_mode() == security_mode
511 {
512 trace!("Found matching endpoint for url {} - {:?}", endpoint_url, e);
513 true
514 } else {
515 false
516 }
517 } else {
518 false
519 }
520 });
521 endpoint.map(|endpoint| endpoint.1)
522 }
523}