1use std::{
2 collections::{BTreeMap, BTreeSet},
3 str::FromStr,
4};
5
6use serde::{Deserialize, Serialize};
7
8use opcua_crypto::SecurityPolicy;
9use opcua_types::MessageSecurityMode;
10
11use super::server::{ServerUserToken, ANONYMOUS_USER_TOKEN_ID};
12
13#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
14pub struct ServerEndpoint {
16 pub path: String,
18 pub security_policy: String,
20 pub security_mode: String,
22 pub security_level: u8,
24 pub password_security_policy: Option<String>,
26 pub user_token_ids: BTreeSet<String>,
28}
29
30#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Hash, Eq)]
31pub struct EndpointIdentifier {
33 pub path: String,
35 pub security_policy: String,
37 pub security_mode: String,
39}
40
41impl From<&ServerEndpoint> for EndpointIdentifier {
42 fn from(value: &ServerEndpoint) -> Self {
43 Self {
44 path: value.path.clone(),
45 security_policy: value.security_policy.clone(),
46 security_mode: value.security_mode.clone(),
47 }
48 }
49}
50
51impl<'a> From<(&'a str, SecurityPolicy, MessageSecurityMode, &'a [&'a str])> for ServerEndpoint {
53 fn from(v: (&'a str, SecurityPolicy, MessageSecurityMode, &'a [&'a str])) -> ServerEndpoint {
54 ServerEndpoint {
55 path: v.0.into(),
56 security_policy: v.1.to_string(),
57 security_mode: v.2.to_string(),
58 security_level: Self::security_level(v.1, v.2),
59 password_security_policy: None,
60 user_token_ids: v.3.iter().map(|id| id.to_string()).collect(),
61 }
62 }
63}
64
65impl ServerEndpoint {
66 pub fn new<T>(
68 path: T,
69 security_policy: SecurityPolicy,
70 security_mode: MessageSecurityMode,
71 user_token_ids: &[String],
72 ) -> Self
73 where
74 T: Into<String>,
75 {
76 ServerEndpoint {
77 path: path.into(),
78 security_policy: security_policy.to_string(),
79 security_mode: security_mode.to_string(),
80 security_level: Self::security_level(security_policy, security_mode),
81 password_security_policy: None,
82 user_token_ids: user_token_ids.iter().cloned().collect(),
83 }
84 }
85
86 fn security_level(security_policy: SecurityPolicy, security_mode: MessageSecurityMode) -> u8 {
88 let security_level = match security_policy {
89 SecurityPolicy::Basic128Rsa15 => 1,
90 SecurityPolicy::Aes128Sha256RsaOaep => 2,
91 SecurityPolicy::Basic256 => 3,
92 SecurityPolicy::Basic256Sha256 => 4,
93 SecurityPolicy::Aes256Sha256RsaPss => 5,
94 _ => 0,
95 };
96 if security_mode == MessageSecurityMode::SignAndEncrypt {
97 security_level + 10
98 } else {
99 security_level
100 }
101 }
102
103 pub fn new_none<T>(path: T, user_token_ids: &[String]) -> Self
105 where
106 T: Into<String>,
107 {
108 Self::new(
109 path,
110 SecurityPolicy::None,
111 MessageSecurityMode::None,
112 user_token_ids,
113 )
114 }
115
116 #[deprecated]
117 pub fn new_basic128rsa15_sign<T>(path: T, user_token_ids: &[String]) -> Self
123 where
124 T: Into<String>,
125 {
126 Self::new(
127 path,
128 SecurityPolicy::Basic128Rsa15,
129 MessageSecurityMode::Sign,
130 user_token_ids,
131 )
132 }
133
134 #[deprecated]
135 pub fn new_basic128rsa15_sign_encrypt<T>(path: T, user_token_ids: &[String]) -> Self
141 where
142 T: Into<String>,
143 {
144 Self::new(
145 path,
146 SecurityPolicy::Basic128Rsa15,
147 MessageSecurityMode::SignAndEncrypt,
148 user_token_ids,
149 )
150 }
151
152 #[deprecated]
153 pub fn new_basic256_sign<T>(path: T, user_token_ids: &[String]) -> Self
159 where
160 T: Into<String>,
161 {
162 Self::new(
163 path,
164 SecurityPolicy::Basic256,
165 MessageSecurityMode::Sign,
166 user_token_ids,
167 )
168 }
169
170 #[deprecated]
171 pub fn new_basic256_sign_encrypt<T>(path: T, user_token_ids: &[String]) -> Self
177 where
178 T: Into<String>,
179 {
180 Self::new(
181 path,
182 SecurityPolicy::Basic256,
183 MessageSecurityMode::SignAndEncrypt,
184 user_token_ids,
185 )
186 }
187
188 #[deprecated]
189 pub fn new_basic256sha256_sign<T>(path: T, user_token_ids: &[String]) -> Self
195 where
196 T: Into<String>,
197 {
198 Self::new(
199 path,
200 SecurityPolicy::Basic256Sha256,
201 MessageSecurityMode::Sign,
202 user_token_ids,
203 )
204 }
205
206 pub fn new_basic256sha256_sign_encrypt<T>(path: T, user_token_ids: &[String]) -> Self
212 where
213 T: Into<String>,
214 {
215 Self::new(
216 path,
217 SecurityPolicy::Basic256Sha256,
218 MessageSecurityMode::SignAndEncrypt,
219 user_token_ids,
220 )
221 }
222
223 pub fn new_aes128_sha256_rsaoaep_sign<T>(path: T, user_token_ids: &[String]) -> Self
225 where
226 T: Into<String>,
227 {
228 Self::new(
229 path,
230 SecurityPolicy::Aes128Sha256RsaOaep,
231 MessageSecurityMode::Sign,
232 user_token_ids,
233 )
234 }
235
236 pub fn new_aes128_sha256_rsaoaep_sign_encrypt<T>(path: T, user_token_ids: &[String]) -> Self
238 where
239 T: Into<String>,
240 {
241 Self::new(
242 path,
243 SecurityPolicy::Aes128Sha256RsaOaep,
244 MessageSecurityMode::SignAndEncrypt,
245 user_token_ids,
246 )
247 }
248
249 pub fn new_aes256_sha256_rsapss_sign<T>(path: T, user_token_ids: &[String]) -> Self
251 where
252 T: Into<String>,
253 {
254 Self::new(
255 path,
256 SecurityPolicy::Aes256Sha256RsaPss,
257 MessageSecurityMode::Sign,
258 user_token_ids,
259 )
260 }
261
262 pub fn new_aes256_sha256_rsapss_sign_encrypt<T>(path: T, user_token_ids: &[String]) -> Self
264 where
265 T: Into<String>,
266 {
267 Self::new(
268 path,
269 SecurityPolicy::Aes256Sha256RsaPss,
270 MessageSecurityMode::SignAndEncrypt,
271 user_token_ids,
272 )
273 }
274
275 pub fn validate(
277 &self,
278 id: &str,
279 user_tokens: &BTreeMap<String, ServerUserToken>,
280 ) -> Result<(), Vec<String>> {
281 let mut errors = Vec::new();
282
283 for id in &self.user_token_ids {
285 if id == ANONYMOUS_USER_TOKEN_ID {
287 continue;
288 }
289 if !user_tokens.contains_key(id) {
290 errors.push(format!("Cannot find user token with id {id}"));
291 }
292 }
293
294 if let Some(ref password_security_policy) = self.password_security_policy {
295 let password_security_policy =
296 SecurityPolicy::from_str(password_security_policy).unwrap();
297 if password_security_policy == SecurityPolicy::Unknown {
298 errors.push(format!("Endpoint {id} is invalid. Password security policy \"{password_security_policy}\" is invalid. Valid values are None, Basic128Rsa15, Basic256, Basic256Sha256"));
299 }
300 }
301
302 let security_policy = SecurityPolicy::from_str(&self.security_policy).unwrap();
304 let security_mode = MessageSecurityMode::from(self.security_mode.as_ref());
305 if security_policy == SecurityPolicy::Unknown {
306 errors.push(format!("Endpoint {} is invalid. Security policy \"{}\" is invalid. Valid values are None, Basic128Rsa15, Basic256, Basic256Sha256, Aes128Sha256RsaOaep, Aes256Sha256RsaPss,", id, self.security_policy));
307 } else if security_mode == MessageSecurityMode::Invalid {
308 errors.push(format!("Endpoint {} is invalid. Security mode \"{}\" is invalid. Valid values are None, Sign, SignAndEncrypt", id, self.security_mode));
309 } else if (security_policy == SecurityPolicy::None
310 && security_mode != MessageSecurityMode::None)
311 || (security_policy != SecurityPolicy::None
312 && security_mode == MessageSecurityMode::None)
313 {
314 errors.push(format!("Endpoint {id} is invalid. Security policy and security mode must both contain None or neither of them should (1)."));
315 } else if security_policy != SecurityPolicy::None
316 && security_mode == MessageSecurityMode::None
317 {
318 errors.push(format!("Endpoint {id} is invalid. Security policy and security mode must both contain None or neither of them should (2)."));
319 }
320
321 if errors.is_empty() {
322 Ok(())
323 } else {
324 Err(errors)
325 }
326 }
327
328 pub fn security_policy(&self) -> SecurityPolicy {
330 SecurityPolicy::from_str(&self.security_policy).unwrap()
331 }
332
333 pub fn message_security_mode(&self) -> MessageSecurityMode {
335 MessageSecurityMode::from(self.security_mode.as_ref())
336 }
337
338 pub fn endpoint_url(&self, base_endpoint: &str) -> String {
340 format!("{}{}", base_endpoint, self.path)
341 }
342
343 pub fn password_security_policy(&self) -> SecurityPolicy {
346 let mut password_security_policy = self.security_policy();
347 if let Some(ref security_policy) = self.password_security_policy {
348 match SecurityPolicy::from_str(security_policy).unwrap() {
349 SecurityPolicy::Unknown => {
350 panic!("Password security policy {security_policy} is unrecognized");
351 }
352 security_policy => {
353 password_security_policy = security_policy;
354 }
355 }
356 }
357 password_security_policy
358 }
359}