1use async_trait::async_trait;
4
5use opcua_crypto::{SecurityPolicy, Thumbprint};
6use opcua_types::{
7 ByteString, Error, MessageSecurityMode, NodeId, StatusCode, UAString, UserTokenPolicy,
8 UserTokenType,
9};
10use tracing::{debug, error};
11
12use crate::identity_token::{
13 POLICY_ID_ANONYMOUS, POLICY_ID_ISSUED_TOKEN_NONE, POLICY_ID_ISSUED_TOKEN_RSA_15,
14 POLICY_ID_ISSUED_TOKEN_RSA_OAEP, POLICY_ID_ISSUED_TOKEN_RSA_OAEP_SHA256,
15 POLICY_ID_USER_PASS_NONE, POLICY_ID_USER_PASS_RSA_15, POLICY_ID_USER_PASS_RSA_OAEP,
16 POLICY_ID_USER_PASS_RSA_OAEP_SHA256, POLICY_ID_X509,
17};
18
19use super::{
20 address_space::AccessLevel, config::ANONYMOUS_USER_TOKEN_ID, ServerEndpoint, ServerUserToken,
21};
22use std::{collections::BTreeMap, fmt::Debug};
23
24#[derive(Clone, PartialEq, Eq)]
26pub struct Password(String);
27
28impl Debug for Password {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 f.debug_tuple("Password").field(&"****").finish()
31 }
32}
33
34impl Password {
35 pub fn new(password: String) -> Self {
37 Self(password)
38 }
39
40 pub fn get(&self) -> &str {
42 &self.0
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct UserToken(pub String);
51
52#[derive(Debug, Clone)]
56pub struct UserSecurityKey {
57 pub token: UserToken,
59 pub security_mode: MessageSecurityMode,
61 pub application_uri: String,
63}
64
65impl UserToken {
66 pub fn is_anonymous(&self) -> bool {
68 self.0 == ANONYMOUS_USER_TOKEN_ID
69 }
70}
71
72#[derive(Default, Debug, Clone)]
74pub struct CoreServerPermissions {
75 pub read_diagnostics: bool,
77}
78
79#[allow(unused)]
80#[async_trait]
81pub trait AuthManager: Send + Sync + 'static {
96 async fn authenticate_anonymous_token(&self, endpoint: &ServerEndpoint) -> Result<(), Error> {
99 Err(Error::new(
100 StatusCode::BadIdentityTokenRejected,
101 "Anonymous identity token unsupported",
102 ))
103 }
104
105 async fn authenticate_username_identity_token(
108 &self,
109 endpoint: &ServerEndpoint,
110 username: &str,
111 password: &Password,
112 ) -> Result<UserToken, Error> {
113 Err(Error::new(
114 StatusCode::BadIdentityTokenRejected,
115 "Username identity token unsupported",
116 ))
117 }
118
119 async fn authenticate_x509_identity_token(
122 &self,
123 endpoint: &ServerEndpoint,
124 signing_thumbprint: &Thumbprint,
125 ) -> Result<UserToken, Error> {
126 Err(Error::new(
127 StatusCode::BadIdentityTokenRejected,
128 "X509 identity token unsupported",
129 ))
130 }
131
132 async fn authenticate_issued_identity_token(
135 &self,
136 endpoint: &ServerEndpoint,
137 token: &ByteString,
138 ) -> Result<UserToken, Error> {
139 Err(Error::new(
140 StatusCode::BadIdentityTokenRejected,
141 "Issued identity token unsupported",
142 ))
143 }
144
145 fn effective_user_access_level(
147 &self,
148 token: &UserToken,
149 user_access_level: AccessLevel,
150 node_id: &NodeId,
151 ) -> AccessLevel {
152 user_access_level
153 }
154
155 fn is_user_executable(&self, token: &UserToken, method_id: &NodeId) -> bool {
158 true
159 }
160
161 fn user_token_policies(&self, endpoint: &ServerEndpoint) -> Vec<UserTokenPolicy>;
164
165 fn supports_anonymous(&self, endpoint: &ServerEndpoint) -> bool {
167 self.user_token_policies(endpoint)
168 .iter()
169 .any(|e| e.token_type == UserTokenType::Anonymous)
170 }
171
172 fn supports_user_pass(&self, endpoint: &ServerEndpoint) -> bool {
174 self.user_token_policies(endpoint)
175 .iter()
176 .any(|e| e.token_type == UserTokenType::UserName)
177 }
178
179 fn supports_x509(&self, endpoint: &ServerEndpoint) -> bool {
181 self.user_token_policies(endpoint)
182 .iter()
183 .any(|e| e.token_type == UserTokenType::Certificate)
184 }
185
186 fn supports_issued_token(&self, endpoint: &ServerEndpoint) -> bool {
188 self.user_token_policies(endpoint)
189 .iter()
190 .any(|e| e.token_type == UserTokenType::IssuedToken)
191 }
192
193 fn core_permissions(&self, token: &UserToken) -> CoreServerPermissions {
195 CoreServerPermissions::default()
196 }
197}
198
199pub struct DefaultAuthenticator {
203 users: BTreeMap<String, ServerUserToken>,
204}
205
206impl DefaultAuthenticator {
207 pub fn new(users: BTreeMap<String, ServerUserToken>) -> Self {
209 Self { users }
210 }
211}
212
213#[async_trait]
214impl AuthManager for DefaultAuthenticator {
215 async fn authenticate_anonymous_token(&self, endpoint: &ServerEndpoint) -> Result<(), Error> {
216 if !endpoint.user_token_ids.contains(ANONYMOUS_USER_TOKEN_ID) {
217 return Err(Error::new(
218 StatusCode::BadIdentityTokenRejected,
219 format!(
220 "Endpoint \"{}\" does not support anonymous authentication",
221 endpoint.path
222 ),
223 ));
224 }
225 Ok(())
226 }
227
228 async fn authenticate_username_identity_token(
229 &self,
230 endpoint: &ServerEndpoint,
231 username: &str,
232 password: &Password,
233 ) -> Result<UserToken, Error> {
234 let token_password = password.get();
235 for user_token_id in &endpoint.user_token_ids {
236 if let Some(server_user_token) = self.users.get(user_token_id) {
237 if server_user_token.is_user_pass() && server_user_token.user == username {
238 let valid = if let Some(server_password) = server_user_token.pass.as_ref() {
240 server_password.as_bytes() == token_password.as_bytes()
241 } else {
242 token_password.is_empty()
243 };
244
245 if !valid {
246 error!(
247 "Cannot authenticate \"{}\", password is invalid",
248 server_user_token.user
249 );
250 return Err(Error::new(
251 StatusCode::BadIdentityTokenRejected,
252 format!("Cannot authenticate user \"{username}\""),
253 ));
254 } else {
255 return Ok(UserToken(user_token_id.clone()));
256 }
257 }
258 }
259 }
260 error!(
261 "Cannot authenticate \"{}\", user not found for endpoint",
262 username
263 );
264 Err(Error::new(
265 StatusCode::BadIdentityTokenRejected,
266 format!("Cannot authenticate \"{username}\""),
267 ))
268 }
269
270 async fn authenticate_x509_identity_token(
271 &self,
272 endpoint: &ServerEndpoint,
273 signing_thumbprint: &Thumbprint,
274 ) -> Result<UserToken, Error> {
275 for user_token_id in &endpoint.user_token_ids {
277 if let Some(server_user_token) = self.users.get(user_token_id) {
278 if let Some(ref user_thumbprint) = server_user_token.thumbprint {
279 if user_thumbprint == signing_thumbprint {
281 return Ok(UserToken(user_token_id.clone()));
282 }
283 }
284 }
285 }
286 Err(Error::new(
287 StatusCode::BadIdentityTokenRejected,
288 "Authentication failed",
289 ))
290 }
291
292 fn user_token_policies(&self, endpoint: &ServerEndpoint) -> Vec<UserTokenPolicy> {
293 let mut user_identity_tokens = Vec::with_capacity(3);
294
295 if endpoint.user_token_ids.contains(ANONYMOUS_USER_TOKEN_ID) {
297 user_identity_tokens.push(UserTokenPolicy {
298 policy_id: UAString::from(POLICY_ID_ANONYMOUS),
299 token_type: UserTokenType::Anonymous,
300 issued_token_type: UAString::null(),
301 issuer_endpoint_url: UAString::null(),
302 security_policy_uri: UAString::null(),
303 });
304 }
305 if endpoint.user_token_ids.iter().any(|id| {
307 id != ANONYMOUS_USER_TOKEN_ID
308 && self.users.get(id).is_some_and(|token| token.is_user_pass())
309 }) {
310 user_identity_tokens.push(UserTokenPolicy {
312 policy_id: user_pass_security_policy_id(endpoint),
313 token_type: UserTokenType::UserName,
314 issued_token_type: UAString::null(),
315 issuer_endpoint_url: UAString::null(),
316 security_policy_uri: user_pass_security_policy_uri(endpoint),
317 });
318 }
319 if endpoint.user_token_ids.iter().any(|id| {
321 id != ANONYMOUS_USER_TOKEN_ID && self.users.get(id).is_some_and(|token| token.is_x509())
322 }) {
323 user_identity_tokens.push(UserTokenPolicy {
324 policy_id: UAString::from(POLICY_ID_X509),
325 token_type: UserTokenType::Certificate,
326 issued_token_type: UAString::null(),
327 issuer_endpoint_url: UAString::null(),
328 security_policy_uri: UAString::from(SecurityPolicy::Basic128Rsa15.to_uri()),
329 });
330 }
331
332 if user_identity_tokens.is_empty() {
333 debug!(
334 "user_identity_tokens() returned zero endpoints for endpoint {} / {} {}",
335 endpoint.path, endpoint.security_policy, endpoint.security_mode
336 );
337 }
338
339 user_identity_tokens
340 }
341
342 fn core_permissions(&self, token: &UserToken) -> CoreServerPermissions {
343 self.users
344 .get(token.0.as_str())
345 .map(|r| CoreServerPermissions {
346 read_diagnostics: r.read_diagnostics,
347 })
348 .unwrap_or_default()
349 }
350}
351
352pub fn user_pass_security_policy_id(endpoint: &ServerEndpoint) -> UAString {
354 match endpoint.password_security_policy() {
355 SecurityPolicy::None => POLICY_ID_USER_PASS_NONE,
356 SecurityPolicy::Basic128Rsa15 => POLICY_ID_USER_PASS_RSA_15,
357 SecurityPolicy::Basic256
358 | SecurityPolicy::Basic256Sha256
359 | SecurityPolicy::Aes128Sha256RsaOaep => POLICY_ID_USER_PASS_RSA_OAEP,
360 SecurityPolicy::Aes256Sha256RsaPss => POLICY_ID_USER_PASS_RSA_OAEP_SHA256,
361 _ => {
362 panic!("Invalid security policy for username and password")
363 }
364 }
365 .into()
366}
367
368pub fn issued_token_security_policy(endpoint: &ServerEndpoint) -> UAString {
370 match endpoint.password_security_policy() {
371 SecurityPolicy::None => POLICY_ID_ISSUED_TOKEN_NONE,
372 SecurityPolicy::Basic128Rsa15 => POLICY_ID_ISSUED_TOKEN_RSA_15,
373 SecurityPolicy::Basic256
374 | SecurityPolicy::Basic256Sha256
375 | SecurityPolicy::Aes128Sha256RsaOaep => POLICY_ID_ISSUED_TOKEN_RSA_OAEP,
376 SecurityPolicy::Aes256Sha256RsaPss => POLICY_ID_ISSUED_TOKEN_RSA_OAEP_SHA256,
377 _ => {
378 panic!("Invalid security policy for username and password")
379 }
380 }
381 .into()
382}
383
384pub fn user_pass_security_policy_uri(_endpoint: &ServerEndpoint) -> UAString {
386 UAString::null()
389}