use std::sync::{Arc, RwLock};
use opcua_core::prelude::*;
use opcua_types::{
node_ids::ObjectId,
profiles,
service_types::{
ApplicationDescription, RegisteredServer, ApplicationType, EndpointDescription,
UserNameIdentityToken, UserTokenPolicy, UserTokenType, X509IdentityToken,
ServerState as ServerStateType,
},
status_code::StatusCode,
};
use crate::config::{ServerConfig, ServerEndpoint};
use crate::diagnostics::ServerDiagnostics;
use crate::callbacks::{RegisterNodes, UnregisterNodes};
const TOKEN_POLICY_ANONYMOUS: &str = "anonymous";
const TOKEN_POLICY_USER_PASS_PLAINTEXT: &str = "userpass_plaintext";
pub struct ServerState {
pub application_uri: UAString,
pub product_uri: UAString,
pub application_name: LocalizedText,
pub base_endpoint: String,
pub start_time: DateTime,
pub namespaces: Vec<String>,
pub servers: Vec<String>,
pub config: Arc<RwLock<ServerConfig>>,
pub server_certificate: Option<X509>,
pub server_pkey: Option<PrivateKey>,
pub last_subscription_id: u32,
pub max_subscriptions: usize,
pub min_publishing_interval: Duration,
pub default_keep_alive_count: u32,
pub max_keep_alive_count: u32,
pub max_lifetime_count: u32,
pub max_method_calls: usize,
pub max_nodes_per_node_management: usize,
pub max_browse_paths_per_translate: usize,
pub state: ServerStateType,
pub abort: bool,
pub diagnostics: Arc<RwLock<ServerDiagnostics>>,
pub(crate) register_nodes_callback: Option<Box<RegisterNodes + Send + Sync>>,
pub(crate) unregister_nodes_callback: Option<Box<UnregisterNodes + Send + Sync>>,
}
impl ServerState {
pub fn endpoints(&self, transport_profile_uris: &Option<Vec<UAString>>) -> Option<Vec<EndpointDescription>> {
debug!("Endpoints requested {:?}", transport_profile_uris);
if let Some(ref transport_profile_uris) = *transport_profile_uris {
if !transport_profile_uris.is_empty() {
let found_binary_transport = transport_profile_uris.iter().find(|profile_uri| {
profile_uri.as_ref() == profiles::TRANSPORT_PROFILE_URI_BINARY
});
if found_binary_transport.is_none() {
error!("Client wants to connect with a non binary transport {:#?}", transport_profile_uris);
return None;
}
}
}
let config = trace_read_lock_unwrap!(self.config);
Some(config.endpoints.iter().map(|(_, e)| {
self.new_endpoint_description(&config, e, true)
}).collect())
}
pub fn endpoint_exists(&self, endpoint_url: &str, security_policy: SecurityPolicy, security_mode: MessageSecurityMode) -> bool {
let config = trace_read_lock_unwrap!(self.config);
config.find_endpoint(endpoint_url, security_policy, security_mode).is_some()
}
pub fn new_endpoint_descriptions(&self, endpoint_url: &str) -> Option<Vec<EndpointDescription>> {
debug!("find_endpoint, url = {}", endpoint_url);
let config = trace_read_lock_unwrap!(self.config);
let base_endpoint_url = config.base_endpoint_url();
let endpoints: Vec<EndpointDescription> = config.endpoints.iter().filter(|&(_, e)| {
url_matches_except_host(&e.endpoint_url(&base_endpoint_url), endpoint_url)
}).map(|(_, e)| self.new_endpoint_description(&config, e, false)).collect();
if endpoints.is_empty() { None } else { Some(endpoints) }
}
fn new_endpoint_description(&self, config: &ServerConfig, endpoint: &ServerEndpoint, all_fields: bool) -> EndpointDescription {
let base_endpoint_url = config.base_endpoint_url();
let mut user_identity_tokens = Vec::with_capacity(2);
if endpoint.supports_anonymous() {
user_identity_tokens.push(UserTokenPolicy {
policy_id: UAString::from(TOKEN_POLICY_ANONYMOUS),
token_type: UserTokenType::Anonymous,
issued_token_type: UAString::null(),
issuer_endpoint_url: UAString::null(),
security_policy_uri: UAString::null(),
});
}
if !endpoint.user_token_ids.is_empty() {
user_identity_tokens.push(UserTokenPolicy {
policy_id: UAString::from(TOKEN_POLICY_USER_PASS_PLAINTEXT),
token_type: UserTokenType::Username,
issued_token_type: UAString::null(),
issuer_endpoint_url: UAString::null(),
security_policy_uri: UAString::from(SecurityPolicy::None.to_uri()),
});
}
let (server, server_certificate) = if all_fields {
(ApplicationDescription {
application_uri: self.application_uri.clone(),
product_uri: self.product_uri.clone(),
application_name: self.application_name.clone(),
application_type: self.application_type(),
gateway_server_uri: self.gateway_server_uri(),
discovery_profile_uri: UAString::null(),
discovery_urls: self.discovery_urls(),
}, self.server_certificate_as_byte_string())
} else {
(ApplicationDescription {
application_uri: UAString::null(),
product_uri: UAString::null(),
application_name: LocalizedText::null(),
application_type: self.application_type(),
gateway_server_uri: self.gateway_server_uri(),
discovery_profile_uri: UAString::null(),
discovery_urls: self.discovery_urls(),
}, ByteString::null())
};
EndpointDescription {
endpoint_url: endpoint.endpoint_url(&base_endpoint_url).into(),
server,
server_certificate,
security_mode: endpoint.message_security_mode(),
security_policy_uri: UAString::from(endpoint.security_policy().to_uri()),
user_identity_tokens: Some(user_identity_tokens),
transport_profile_uri: UAString::from(profiles::TRANSPORT_PROFILE_URI_BINARY),
security_level: endpoint.security_level,
}
}
pub fn discovery_urls(&self) -> Option<Vec<UAString>> {
let config = trace_read_lock_unwrap!(self.config);
if config.discovery_urls.is_empty() {
None
} else {
Some(config.discovery_urls.iter().map(|url| UAString::from(url.as_ref())).collect())
}
}
pub fn application_type(&self) -> ApplicationType { ApplicationType::Server }
pub fn gateway_server_uri(&self) -> UAString { UAString::null() }
pub fn abort(&mut self) {
info!("Server has been told to abort");
self.abort = true;
self.state = ServerStateType::Shutdown;
}
pub fn state(&self) -> ServerStateType { self.state }
pub fn set_state(&mut self, state: ServerStateType) {
self.state = state;
}
pub fn is_abort(&self) -> bool { self.abort }
pub fn is_running(&self) -> bool { self.state == ServerStateType::Running }
pub fn max_method_calls(&self) -> usize {
self.max_method_calls
}
pub fn max_nodes_per_node_management(&self) -> usize {
self.max_nodes_per_node_management
}
pub fn max_browse_paths_per_translate(&self) -> usize {
self.max_browse_paths_per_translate
}
pub fn server_certificate_as_byte_string(&self) -> ByteString {
if let Some(ref server_certificate) = self.server_certificate {
server_certificate.as_byte_string()
} else {
ByteString::null()
}
}
pub fn registered_server(&self) -> RegisteredServer {
let server_uri = self.application_uri.clone();
let product_uri = self.product_uri.clone();
let gateway_server_uri = self.gateway_server_uri();
let discovery_urls = self.discovery_urls();
let server_type = self.application_type();
let is_online = self.is_running();
let server_names = Some(vec![self.application_name.clone()]);
RegisteredServer {
server_uri,
product_uri,
server_names,
server_type,
gateway_server_uri,
discovery_urls,
semaphore_file_path: UAString::null(),
is_online,
}
}
pub fn create_subscription_id(&mut self) -> u32 {
self.last_subscription_id += 1;
self.last_subscription_id
}
pub fn authenticate_endpoint(&self, endpoint_url: &str, security_policy: SecurityPolicy, security_mode: MessageSecurityMode, user_identity_token: &ExtensionObject) -> StatusCode {
let config = trace_read_lock_unwrap!(self.config);
let decoding_limits = config.decoding_limits();
if let Some(endpoint) = config.find_endpoint(endpoint_url, security_policy, security_mode) {
if user_identity_token.is_null() || user_identity_token.is_empty() {
Self::authenticate_anonymous_token(endpoint)
} else {
if let Ok(object_id) = user_identity_token.node_id.as_object_id() {
match object_id {
ObjectId::AnonymousIdentityToken_Encoding_DefaultBinary => {
Self::authenticate_anonymous_token(endpoint)
}
ObjectId::UserNameIdentityToken_Encoding_DefaultBinary => {
let result = user_identity_token.decode_inner::<UserNameIdentityToken>(&decoding_limits);
if let Ok(token) = result {
self.authenticate_username_identity_token(&config, endpoint, &token)
} else {
error!("User name identity token could not be decoded");
StatusCode::BadIdentityTokenInvalid
}
}
ObjectId::X509IdentityToken_Encoding_DefaultBinary => {
let result = user_identity_token.decode_inner::<X509IdentityToken>(&decoding_limits);
if let Ok(_) = result {
error!("X509 identity token type is not supported");
StatusCode::BadIdentityTokenRejected
} else {
error!("X509 identity token could not be decoded");
StatusCode::BadIdentityTokenInvalid
}
}
_ => {
error!("User identity token type {:?} is unrecognized", object_id);
StatusCode::BadIdentityTokenInvalid
}
}
} else {
error!("Cannot read user identity token");
StatusCode::BadIdentityTokenInvalid
}
}
} else {
error!("Cannot find endpoint that matches path \"{}\", security policy {:?}, and security mode {:?}", endpoint_url, security_policy, security_mode);
StatusCode::BadTcpEndpointUrlInvalid
}
}
pub fn set_register_nodes_callbacks(&mut self, register_nodes_callback: Box<RegisterNodes + Send + Sync>, unregister_nodes_callback: Box<UnregisterNodes + Send + Sync>) {
self.register_nodes_callback = Some(register_nodes_callback);
self.unregister_nodes_callback = Some(unregister_nodes_callback);
}
fn authenticate_anonymous_token(endpoint: &ServerEndpoint) -> StatusCode {
if endpoint.supports_anonymous() {
debug!("Anonymous identity is authenticated");
StatusCode::Good
} else {
error!("Endpoint \"{}\" does not support anonymous authentication", endpoint.path);
StatusCode::BadIdentityTokenRejected
}
}
fn authenticate_username_identity_token(&self, config: &ServerConfig, endpoint: &ServerEndpoint, token: &UserNameIdentityToken) -> StatusCode {
if !token.encryption_algorithm.is_null() {
error!("Only unencrypted passwords are supported, {:?}", token);
StatusCode::BadIdentityTokenInvalid
} else if token.user_name.is_null() {
error!("User identify token supplies no user name");
StatusCode::BadIdentityTokenInvalid
} else {
for user_token_id in &endpoint.user_token_ids {
if let Some(server_user_token) = config.user_tokens.get(user_token_id) {
if &server_user_token.user == token.user_name.as_ref() {
let result = if server_user_token.pass.is_none() {
token.authenticate(&server_user_token.user, b"")
} else {
let server_password = server_user_token.pass.as_ref().unwrap().as_bytes();
token.authenticate(&server_user_token.user, server_password)
};
let valid = result.is_ok();
if !valid {
error!("Cannot authenticate \"{}\", password is invalid", server_user_token.user);
return StatusCode::BadIdentityTokenRejected;
} else {
return StatusCode::Good;
}
}
}
}
error!("Cannot authenticate \"{}\", user not found for endpoint", token.user_name);
StatusCode::BadIdentityTokenRejected
}
}
}