diff --git a/shared/grpc_clients/build.rs b/shared/grpc_clients/build.rs index eff4ba98a..bd2e351a8 100644 --- a/shared/grpc_clients/build.rs +++ b/shared/grpc_clients/build.rs @@ -1,12 +1,13 @@ fn main() { tonic_build::configure() .build_server(false) .compile( &[ "../protos/identity_client.proto", "../protos/identity_authenticated.proto", + "../protos/tunnelbroker.proto", ], &["../protos"], ) .unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e)); } diff --git a/shared/grpc_clients/src/identity/authenticated.rs b/shared/grpc_clients/src/identity/authenticated.rs index 50870af05..b5ebb4a00 100644 --- a/shared/grpc_clients/src/identity/authenticated.rs +++ b/shared/grpc_clients/src/identity/authenticated.rs @@ -1,60 +1,60 @@ use super::protos::authenticated::identity_client_service_client::IdentityClientServiceClient as AuthClient; use tonic::{ codegen::InterceptedService, metadata::{errors::InvalidMetadataValue, Ascii, MetadataValue}, service::Interceptor, transport::Channel, Request, Status, }; use crate::error::Error; pub struct AuthLayer { user_id: String, device_id: String, access_token: String, } trait ToMetadataValueAscii { fn parse_to_ascii(&self) -> Result, Status>; } impl ToMetadataValueAscii for str { fn parse_to_ascii(&self) -> Result, Status> { self.parse().map_err(|e: InvalidMetadataValue| { Status::invalid_argument(format!( "Non-Ascii character present in metadata value: {}", e )) }) } } impl Interceptor for AuthLayer { fn call(&mut self, mut request: Request<()>) -> Result, Status> { let metadata = request.metadata_mut(); metadata.insert("user_id", self.user_id.parse_to_ascii()?); metadata.insert("device_id", self.device_id.parse_to_ascii()?); metadata.insert("access_token", self.access_token.parse_to_ascii()?); Ok(request) } } pub async fn get_auth_client( url: &str, user_id: String, device_id: String, access_token: String, ) -> Result>, Error> { - use super::get_identity_service_channel; + use crate::get_grpc_service_channel; - let channel = get_identity_service_channel(url).await?; + let channel = get_grpc_service_channel(url).await?; let interceptor = AuthLayer { user_id, device_id, access_token, }; Ok(AuthClient::with_interceptor(channel, interceptor)) } diff --git a/shared/grpc_clients/src/identity/mod.rs b/shared/grpc_clients/src/identity/mod.rs index 66f886f7f..398a96d8d 100644 --- a/shared/grpc_clients/src/identity/mod.rs +++ b/shared/grpc_clients/src/identity/mod.rs @@ -1,40 +1,17 @@ pub mod authenticated; pub mod unauthenticated; pub mod protos { // This must be named client for authenticated generated code pub mod client { tonic::include_proto!("identity.client"); } pub use client as unauthenticated; pub mod authenticated { tonic::include_proto!("identity.authenticated"); } } -use crate::error::Error; -use tonic::transport::Channel; -use tonic::transport::{Certificate, ClientTlsConfig}; -use tracing::{self, info}; - pub use authenticated::get_auth_client; pub use unauthenticated::get_unauthenticated_client; - -pub(crate) async fn get_identity_service_channel( - url: &str, -) -> Result { - let ca_cert = crate::get_ca_cert_contents().expect("Unable to get CA bundle"); - - info!("Connecting to identity service at {}", url); - let mut channel = Channel::from_shared(url.to_string())?; - - // tls_config will fail if the underlying URI is only http:// - if url.starts_with("https:") { - channel = channel.tls_config( - ClientTlsConfig::new().ca_certificate(Certificate::from_pem(&ca_cert)), - )? - } - - Ok(channel.connect().await?) -} diff --git a/shared/grpc_clients/src/identity/unauthenticated.rs b/shared/grpc_clients/src/identity/unauthenticated.rs index e5760e1c6..4e9d408e0 100644 --- a/shared/grpc_clients/src/identity/unauthenticated.rs +++ b/shared/grpc_clients/src/identity/unauthenticated.rs @@ -1,11 +1,11 @@ use tonic::transport::Channel; use super::protos::client::identity_client_service_client::IdentityClientServiceClient; use crate::error::Error; pub async fn get_unauthenticated_client( url: &str, ) -> Result, Error> { - let channel = super::get_identity_service_channel(url).await?; + let channel = crate::get_grpc_service_channel(url).await?; Ok(IdentityClientServiceClient::new(channel)) } diff --git a/shared/grpc_clients/src/lib.rs b/shared/grpc_clients/src/lib.rs index 2c356edbc..4de7dcfec 100644 --- a/shared/grpc_clients/src/lib.rs +++ b/shared/grpc_clients/src/lib.rs @@ -1,21 +1,42 @@ pub mod error; pub mod identity; +pub mod tunnelbroker; +use error::Error; use std::path::Path; +use tonic::transport::{Certificate, Channel, ClientTlsConfig}; +use tracing::info; const CERT_PATHS: &'static [&'static str] = &[ // MacOS and newer Ubuntu "/etc/ssl/cert.pem", // Common CA cert paths "/etc/ssl/certs/ca-bundle.crt", "/etc/ssl/certs/ca-certificates.crt", ]; pub(crate) fn get_ca_cert_contents() -> Option { CERT_PATHS .iter() .map(Path::new) .filter(|p| p.exists()) .filter_map(|f| std::fs::read_to_string(f).ok()) .next() } +pub(crate) async fn get_grpc_service_channel( + url: &str, +) -> Result { + let ca_cert = crate::get_ca_cert_contents().expect("Unable to get CA bundle"); + + info!("Connecting to gRPC service at {}", url); + let mut channel = Channel::from_shared(url.to_string())?; + + // tls_config will fail if the underlying URI is only http:// + if url.starts_with("https:") { + channel = channel.tls_config( + ClientTlsConfig::new().ca_certificate(Certificate::from_pem(&ca_cert)), + )? + } + + Ok(channel.connect().await?) +} diff --git a/shared/grpc_clients/src/tunnelbroker/mod.rs b/shared/grpc_clients/src/tunnelbroker/mod.rs new file mode 100644 index 000000000..fe88db2b7 --- /dev/null +++ b/shared/grpc_clients/src/tunnelbroker/mod.rs @@ -0,0 +1,14 @@ +pub mod protos { + tonic::include_proto!("tunnelbroker"); +} +use protos::tunnelbroker_service_client::TunnelbrokerServiceClient; +use tonic::transport::Channel; + +use crate::error::Error; + +pub async fn create_tunnelbroker_client( + url: &str, +) -> Result, Error> { + let channel = crate::get_grpc_service_channel(url).await?; + Ok(TunnelbrokerServiceClient::new(channel)) +}