Skip to main content

opcua_client/
identity_token.rs

1use std::{path::Path, sync::Arc};
2
3use async_trait::async_trait;
4use opcua_crypto::{CertificateStore, PrivateKey, X509};
5use opcua_types::{ByteString, Error, StatusCode};
6
7#[async_trait]
8/// Source for an issued token. Since each re-authentication when using
9/// issued tokens may require a new token.
10pub trait IssuedTokenSource: Send + Sync {
11    /// Get a valid issued token. This may be a cached token,
12    /// or a new one if the cache is empty or expired.
13    async fn get_issued_token(&self) -> Result<ByteString, Error>;
14}
15
16#[async_trait]
17impl IssuedTokenSource for ByteString {
18    async fn get_issued_token(&self) -> Result<ByteString, Error> {
19        Ok(self.clone())
20    }
21}
22
23/// Wrapper for an issued token source.
24#[derive(Clone)]
25pub struct IssuedTokenWrapper(pub(crate) Arc<dyn IssuedTokenSource>);
26
27impl IssuedTokenWrapper {
28    /// Create a new issued token wrapper from a reference to an issued token source.
29    pub fn new(token_source: Arc<dyn IssuedTokenSource>) -> Self {
30        Self(token_source)
31    }
32
33    /// Create a new issued token wrapper.
34    pub fn new_source(token_source: impl IssuedTokenSource + 'static) -> Self {
35        Self(Arc::new(token_source))
36    }
37}
38
39impl std::fmt::Debug for IssuedTokenWrapper {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        f.debug_tuple("IssuedTokenSource").finish()
42    }
43}
44
45#[derive(Clone)]
46/// A wrapper around a password as string. This intentionally implements
47/// debug in a way that does not expose the password.
48pub struct Password(pub String);
49
50impl Password {
51    /// Create a new password from a string.
52    pub fn new(password: impl Into<String>) -> Self {
53        Password(password.into())
54    }
55}
56
57impl<T> From<T> for Password
58where
59    T: Into<String>,
60{
61    fn from(value: T) -> Self {
62        Password(value.into())
63    }
64}
65
66impl std::fmt::Debug for Password {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        f.debug_tuple("Password").field(&"*****").finish()
69    }
70}
71
72#[derive(Debug, Clone)]
73/// Client-side identity token representation.
74pub enum IdentityToken {
75    /// Anonymous identity token
76    Anonymous,
77    /// User name and a password
78    UserName(String, Password),
79    /// X5090 cert and private key.
80    X509(Box<X509>, Box<PrivateKey>),
81    /// Issued token
82    IssuedToken(IssuedTokenWrapper),
83}
84
85impl IdentityToken {
86    /// Create a new anonymous identity token.
87    pub fn new_anonymous() -> Self {
88        IdentityToken::Anonymous
89    }
90    /// Create a new user name identity token.
91    pub fn new_user_name(user_name: impl Into<String>, password: impl Into<Password>) -> Self {
92        IdentityToken::UserName(user_name.into(), password.into())
93    }
94
95    /// Create a new x509 identity token.
96    pub fn new_x509(cert: X509, private_key: PrivateKey) -> Self {
97        IdentityToken::X509(Box::new(cert), Box::new(private_key))
98    }
99
100    /// Create a new x509 identity token from a path to a certificate and private key.
101    pub fn new_x509_path(
102        cert_path: impl AsRef<Path>,
103        key_path: impl AsRef<Path>,
104    ) -> Result<Self, Error> {
105        let cert = CertificateStore::read_cert(cert_path.as_ref())
106            .map_err(|e| Error::new(StatusCode::Bad, e))?;
107        let private_key = CertificateStore::read_pkey(key_path.as_ref())
108            .map_err(|e| Error::new(StatusCode::Bad, e))?;
109        Ok(IdentityToken::X509(Box::new(cert), Box::new(private_key)))
110    }
111
112    /// Create a new issued token based identity token.
113    pub fn new_issued_token(token_source: impl IssuedTokenSource + 'static) -> Self {
114        IdentityToken::IssuedToken(IssuedTokenWrapper::new_source(token_source))
115    }
116
117    /// Create a new issued token based identity token from a shared reference.
118    pub fn new_issued_token_arc(token_source: Arc<dyn IssuedTokenSource>) -> Self {
119        IdentityToken::IssuedToken(IssuedTokenWrapper::new(token_source))
120    }
121}