diff --git a/implementations/rust/ockam/ockam_api/src/nodes/connection/mod.rs b/implementations/rust/ockam/ockam_api/src/nodes/connection/mod.rs index 3d97e591ea5..aaddc827ce6 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/connection/mod.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/connection/mod.rs @@ -19,6 +19,7 @@ pub(crate) use plain_tcp::PlainTcpInstantiator; pub(crate) use project::ProjectInstantiator; pub(crate) use secure::SecureChannelInstantiator; use std::fmt::{Debug, Formatter}; +use std::sync::Arc; #[derive(Clone)] pub struct Connection { @@ -181,13 +182,49 @@ pub trait Instantiator: Send + Sync + 'static { /// The returned [`Changes`] will be used to update the builder state. async fn instantiate( &self, - ctx: &Context, - node_manager: &NodeManager, + context: &Context, transport_route: Route, extracted: (MultiAddr, MultiAddr, MultiAddr), ) -> Result; } +/// Aggregates multiple [`Instantiator`]s, having a single object containing +/// all runtime dependencies. +pub struct ConnectionInstantiator { + instantiator: Vec>, +} + +impl ConnectionInstantiator { + pub fn new() -> Self { + ConnectionInstantiator { + instantiator: vec![], + } + } + + pub fn add(mut self, instantiator: impl Instantiator) -> Self { + self.instantiator.push(Arc::new(instantiator)); + self + } + + /// Resolve project ID (if any), create secure channel (if needed) and create a tcp connection + /// Returns [`Connection`] + pub async fn connect(&self, ctx: &Context, addr: &MultiAddr) -> Result { + debug!("connecting to {}", &addr); + + let mut connection_builder = ConnectionBuilder::new(addr.clone()); + for instantiator in self.instantiator.clone() { + connection_builder = connection_builder + .instantiate(ctx, instantiator.as_ref()) + .await?; + } + let connection = connection_builder.build(); + connection.add_default_consumers(ctx); + + debug!("connected to {connection:?}"); + Ok(connection) + } +} + impl ConnectionBuilder { pub fn new(multi_addr: MultiAddr) -> Self { ConnectionBuilder { @@ -214,11 +251,10 @@ impl ConnectionBuilder { /// Used to instantiate a connection from a [`MultiAddr`] /// when called multiple times the instantiator order matters and it's up to the /// user make sure higher protocol abstraction are called before lower level ones - pub async fn instantiate( + pub async fn instantiate( mut self, ctx: &Context, - node_manager: &NodeManager, - instantiator: impl Instantiator, + instantiator: &T, ) -> Result { //executing a regex-like search, shifting the starting point one by one //not efficient by any mean, but it shouldn't be an issue @@ -240,7 +276,6 @@ impl ConnectionBuilder { let mut changes = instantiator .instantiate( ctx, - node_manager, self.transport_route.clone(), self.extract(start, instantiator.matches().len()), ) diff --git a/implementations/rust/ockam/ockam_api/src/nodes/connection/plain_tcp.rs b/implementations/rust/ockam/ockam_api/src/nodes/connection/plain_tcp.rs index faaa3eb4199..baf94ea597d 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/connection/plain_tcp.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/connection/plain_tcp.rs @@ -2,18 +2,20 @@ use crate::error::ApiError; use crate::nodes::connection::{Changes, ConnectionBuilder, Instantiator}; use crate::{multiaddr_to_route, route_to_multiaddr}; -use crate::nodes::NodeManager; -use ockam_core::{async_trait, Error, Route}; +use ockam_core::{async_trait, Route}; use ockam_multiaddr::proto::{DnsAddr, Ip4, Ip6, Tcp}; use ockam_multiaddr::{Match, MultiAddr, Protocol}; use ockam_node::Context; +use ockam_transport_tcp::TcpTransport; /// Creates the tcp connection. -pub(crate) struct PlainTcpInstantiator {} +pub(crate) struct PlainTcpInstantiator { + tcp_transport: TcpTransport, +} impl PlainTcpInstantiator { - pub(crate) fn new() -> Self { - Self {} + pub(crate) fn new(tcp_transport: TcpTransport) -> Self { + Self { tcp_transport } } } @@ -29,14 +31,13 @@ impl Instantiator for PlainTcpInstantiator { async fn instantiate( &self, - _ctx: &Context, - node_manager: &NodeManager, + _context: &Context, _transport_route: Route, extracted: (MultiAddr, MultiAddr, MultiAddr), - ) -> Result { + ) -> Result { let (before, tcp_piece, after) = extracted; - let mut tcp = multiaddr_to_route(&tcp_piece, &node_manager.tcp_transport) + let mut tcp = multiaddr_to_route(&tcp_piece, &self.tcp_transport) .await .ok_or_else(|| { ApiError::core(format!( diff --git a/implementations/rust/ockam/ockam_api/src/nodes/connection/project.rs b/implementations/rust/ockam/ockam_api/src/nodes/connection/project.rs index 154d6cba092..644447f2ebc 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/connection/project.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/connection/project.rs @@ -1,28 +1,40 @@ use crate::error::ApiError; use crate::nodes::connection::{Changes, Instantiator}; -use crate::nodes::NodeManager; -use crate::{multiaddr_to_route, try_address_to_multiaddr}; +use crate::{multiaddr_to_route, try_address_to_multiaddr, CliState}; +use std::sync::Arc; -use ockam_core::{async_trait, Error, Route}; +use ockam_core::{async_trait, Route}; use ockam_multiaddr::proto::Project; use ockam_multiaddr::{Match, MultiAddr, Protocol}; use ockam_node::Context; -use crate::nodes::service::SecureChannelType; -use ockam::identity::Identifier; +use ockam::identity::{Identifier, SecureChannelOptions, SecureChannels}; +use ockam_transport_tcp::TcpTransport; use std::time::Duration; /// Creates a secure connection to the project using provided credential pub(crate) struct ProjectInstantiator { identifier: Identifier, timeout: Option, + cli_state: CliState, + secure_channels: Arc, + tcp_transport: TcpTransport, } impl ProjectInstantiator { - pub fn new(identifier: Identifier, timeout: Option) -> Self { + pub fn new( + identifier: Identifier, + timeout: Option, + cli_state: CliState, + secure_channels: Arc, + tcp_transport: TcpTransport, + ) -> Self { Self { identifier, timeout, + cli_state, + secure_channels, + tcp_transport, } } } @@ -35,11 +47,10 @@ impl Instantiator for ProjectInstantiator { async fn instantiate( &self, - ctx: &Context, - node_manager: &NodeManager, + context: &Context, _transport_route: Route, extracted: (MultiAddr, MultiAddr, MultiAddr), - ) -> Result { + ) -> Result { let (_before, project_piece, after) = extracted; let project_protocol_value = project_piece @@ -50,11 +61,25 @@ impl Instantiator for ProjectInstantiator { .cast::() .ok_or_else(|| ApiError::core("invalid project protocol in multiaddr"))?; - let (project_multiaddr, project_identifier) = - node_manager.resolve_project(&project).await?; + let (project_multiaddr, project_identifier) = self + .cli_state + .projects() + .get_project_by_name(&project) + .await + .map(|project| { + ( + project.project_multiaddr().cloned(), + project + .project_identifier() + .ok_or_else(|| ApiError::core("project identifier is missing")), + ) + })?; + + let project_identifier = project_identifier?; + let project_multiaddr = project_multiaddr?; debug!(addr = %project_multiaddr, "creating secure channel"); - let tcp = multiaddr_to_route(&project_multiaddr, &node_manager.tcp_transport) + let tcp = multiaddr_to_route(&project_multiaddr, &self.tcp_transport) .await .ok_or_else(|| { ApiError::core(format!( @@ -63,27 +88,29 @@ impl Instantiator for ProjectInstantiator { })?; debug!("create a secure channel to the project {project_identifier}"); - let sc = node_manager - .create_secure_channel_internal( - ctx, - tcp.route, - &self.identifier.clone(), - Some(vec![project_identifier]), - None, - self.timeout, - SecureChannelType::KeyExchangeAndMessages, - ) + + let options = SecureChannelOptions::new().with_authority(project_identifier); + let options = if let Some(timeout) = self.timeout { + options.with_timeout(timeout) + } else { + options + }; + + let secure_channel = self + .secure_channels + .create_secure_channel(context, &self.identifier.clone(), tcp.route, options) .await?; - // when creating a secure channel we want the route to pass through that + // when creating a secure channel, we want the route to pass through that // ignoring previous steps, since they will be implicit - let mut current_multiaddr = try_address_to_multiaddr(sc.encryptor_address()).unwrap(); + let mut current_multiaddr = + try_address_to_multiaddr(secure_channel.encryptor_address()).unwrap(); current_multiaddr.try_extend(after.iter())?; Ok(Changes { - flow_control_id: Some(sc.flow_control_id().clone()), + flow_control_id: Some(secure_channel.flow_control_id().clone()), current_multiaddr, - secure_channel_encryptors: vec![sc.encryptor_address().clone()], + secure_channel_encryptors: vec![secure_channel.encryptor_address().clone()], tcp_connection: tcp.tcp_connection, }) } diff --git a/implementations/rust/ockam/ockam_api/src/nodes/connection/secure.rs b/implementations/rust/ockam/ockam_api/src/nodes/connection/secure.rs index 7903c177764..9592d702a05 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/connection/secure.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/connection/secure.rs @@ -1,12 +1,14 @@ +use std::sync::Arc; use std::time::Duration; use crate::nodes::connection::{Changes, Instantiator}; -use crate::nodes::NodeManager; use crate::{local_multiaddr_to_route, try_address_to_multiaddr}; -use crate::nodes::service::SecureChannelType; -use ockam::identity::Identifier; -use ockam_core::{async_trait, route, AsyncTryClone, Error, Route}; +use ockam::identity::{ + Identifier, SecureChannelOptions, SecureChannels, TrustEveryonePolicy, + TrustMultiIdentifiersPolicy, +}; +use ockam_core::{async_trait, route, Route}; use ockam_multiaddr::proto::Secure; use ockam_multiaddr::{Match, MultiAddr, Protocol}; use ockam_node::Context; @@ -16,6 +18,8 @@ pub(crate) struct SecureChannelInstantiator { identifier: Identifier, authorized_identities: Option>, timeout: Option, + secure_channels: Arc, + authority: Option, } impl SecureChannelInstantiator { @@ -23,11 +27,15 @@ impl SecureChannelInstantiator { identifier: &Identifier, timeout: Option, authorized_identities: Option>, + authority: Option, + secure_channels: Arc, ) -> Self { Self { identifier: identifier.clone(), authorized_identities, + authority, timeout, + secure_channels, } } } @@ -40,39 +48,53 @@ impl Instantiator for SecureChannelInstantiator { async fn instantiate( &self, - ctx: &Context, - node_manager: &NodeManager, + context: &Context, transport_route: Route, extracted: (MultiAddr, MultiAddr, MultiAddr), - ) -> Result { + ) -> Result { let (_before, secure_piece, after) = extracted; debug!(%secure_piece, %transport_route, "creating secure channel"); let route = local_multiaddr_to_route(&secure_piece)?; - let sc_ctx = ctx.async_try_clone().await?; - let sc = node_manager - .create_secure_channel_internal( - &sc_ctx, - //the transport route is needed to reach the secure channel listener - //since it can be in another node - route![transport_route, route], + let options = SecureChannelOptions::new(); + + let options = match self.authorized_identities.clone() { + Some(ids) => options.with_trust_policy(TrustMultiIdentifiersPolicy::new(ids)), + None => options.with_trust_policy(TrustEveryonePolicy), + }; + + let options = if let Some(authority) = self.authority.clone() { + options.with_authority(authority) + } else { + options + }; + + let options = if let Some(timeout) = self.timeout { + options.with_timeout(timeout) + } else { + options + }; + + let secure_channel = self + .secure_channels + .create_secure_channel( + context, &self.identifier, - self.authorized_identities.clone(), - None, - self.timeout, - SecureChannelType::KeyExchangeAndMessages, + route![transport_route, route], + options, ) .await?; // when creating a secure channel we want the route to pass through that // ignoring previous steps, since they will be implicit - let mut current_multiaddr = try_address_to_multiaddr(sc.encryptor_address()).unwrap(); + let mut current_multiaddr = + try_address_to_multiaddr(secure_channel.encryptor_address()).unwrap(); current_multiaddr.try_extend(after.iter())?; Ok(Changes { current_multiaddr, - flow_control_id: Some(sc.flow_control_id().clone()), - secure_channel_encryptors: vec![sc.encryptor_address().clone()], + flow_control_id: Some(secure_channel.flow_control_id().clone()), + secure_channel_encryptors: vec![secure_channel.encryptor_address().clone()], tcp_connection: None, }) } diff --git a/implementations/rust/ockam/ockam_api/src/nodes/service/manager.rs b/implementations/rust/ockam/ockam_api/src/nodes/service/manager.rs index 158cb2d6d67..313093c4ce1 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/service/manager.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/service/manager.rs @@ -1,7 +1,7 @@ use crate::cloud::project::Project; use crate::cloud::{AuthorityNodeClient, ControllerClient, CredentialsEnabled, ProjectNodeClient}; use crate::nodes::connection::{ - Connection, ConnectionBuilder, PlainTcpInstantiator, ProjectInstantiator, + Connection, ConnectionInstantiator, PlainTcpInstantiator, ProjectInstantiator, SecureChannelInstantiator, }; use crate::nodes::models::portal::OutletStatus; @@ -292,41 +292,25 @@ impl NodeManager { timeout: Option, ) -> ockam_core::Result { let authorized = authorized.map(|authorized| vec![authorized]); - self.connect(ctx, addr, identifier, authorized, timeout) - .await - } - - /// Resolve project ID (if any), create secure channel (if needed) and create a tcp connection - /// Returns [`Connection`] - async fn connect( - &self, - ctx: &Context, - addr: &MultiAddr, - identifier: Identifier, - authorized: Option>, - timeout: Option, - ) -> ockam_core::Result { - debug!(?timeout, "connecting to {}", &addr); - let connection = ConnectionBuilder::new(addr.clone()) - .instantiate( - ctx, - self, - ProjectInstantiator::new(identifier.clone(), timeout), - ) - .await? - .instantiate(ctx, self, PlainTcpInstantiator::new()) - .await? - .instantiate( - ctx, - self, - SecureChannelInstantiator::new(&identifier, timeout, authorized), - ) - .await? - .build(); - connection.add_default_consumers(ctx); - debug!("connected to {connection:?}"); - Ok(connection) + let connection_instantiator = ConnectionInstantiator::new() + .add(ProjectInstantiator::new( + identifier.clone(), + timeout, + self.cli_state.clone(), + self.secure_channels.clone(), + self.tcp_transport.clone(), + )) + .add(PlainTcpInstantiator::new(self.tcp_transport.clone())) + .add(SecureChannelInstantiator::new( + &identifier, + timeout, + authorized, + self.project_authority(), + self.secure_channels.clone(), + )); + + connection_instantiator.connect(ctx, addr).await } pub(crate) async fn resolve_project(