Skip to content

Commit

Permalink
found method, yay
Browse files Browse the repository at this point in the history
  • Loading branch information
Christoph Bühler committed Jan 24, 2024
1 parent ed1d157 commit 473c465
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 62 deletions.
126 changes: 69 additions & 57 deletions src/api/clients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ use custom_error::custom_error;
use tonic::codegen::InterceptedService;
use tonic::service::Interceptor;
use tonic::transport::{Channel, Endpoint};
use tonic::{Request, Status};

use crate::api::interceptors::{NoopInterceptor, ServiceAccountInterceptor};
use crate::api::interceptors::{
AccessTokenInterceptor, ChainedInterceptor, ServiceAccountInterceptor,
};
use crate::credentials::{AuthenticationOptions, ServiceAccount};

use super::zitadel::{
admin::v1::admin_service_client::AdminServiceClient,
Expand Down Expand Up @@ -63,83 +65,93 @@ pub async fn create_admin_client(
Ok(AdminServiceClient::new(channel))
}

enum AuthType {
None,
AccessToken(String),
ServiceAccount(ServiceAccount, Option<AuthenticationOptions>),
}

pub struct ClientBuilder {
api_endpoint: String,
interceptor: Option<Intercept>,
// access_token: Option<String>,
// service_account: Option<ServiceAccount>,
// auth_options: Option<AuthenticationOptions>,
auth_type: AuthType,
}

type Intercept = Box<dyn Fn(Request<()>) -> Result<Request<()>, Status>>;

impl ClientBuilder {
pub fn new(api_endpoint: &str) -> Self {
Self {
api_endpoint: api_endpoint.to_string(),
interceptor: None,
// access_token: None,
// service_account: None,
// auth_options: None,
auth_type: AuthType::None,
}
}

pub fn with_access_token(mut self, access_token: &str) -> Self {
let access_token = access_token.to_string();
self.interceptor = Some(Box::new(move |mut req: Request<()>| {
req.metadata_mut()
.insert("authorization", access_token.parse().unwrap());
Ok(req)
}));
// self.access_token = Some(access_token.to_string());
// self.service_account = None;
// self.auth_options = None;
// TODO: https://github.com/hyperium/tonic/issues/730
// https://blog.cloudflare.com/pin-and-unpin-in-rust
self.auth_type = AuthType::AccessToken(access_token.to_string());
self
}

// pub fn with_service_account(
// mut self,
// service_account: &ServiceAccount,
// auth_options: Option<AuthenticationOptions>,
// ) -> Self {
// self.access_token = None;
// self.service_account = Some(service_account.clone());
// self.auth_options = auth_options;
// self
// }
pub fn with_service_account(
mut self,
service_account: &ServiceAccount,
auth_options: Option<AuthenticationOptions>,
) -> Self {
self.auth_type = AuthType::ServiceAccount(service_account.clone(), auth_options);
self
}

pub async fn build_admin_client(
self,
) -> Result<AdminServiceClient<InterceptedService<Channel, Intercept>>, Box<dyn Error>> {
&self,
) -> Result<AdminServiceClient<InterceptedService<Channel, ChainedInterceptor>>, Box<dyn Error>>
{
let channel = get_channel(&self.api_endpoint).await?;
Ok(AdminServiceClient::with_interceptor(
channel,
self.interceptor
.unwrap_or(Box::new(move |req: Request<()>| Ok(req))),
self.get_chained_interceptor(),
))
}

pub async fn build_auth_client(
&self,
) -> Result<AuthServiceClient<InterceptedService<Channel, ChainedInterceptor>>, Box<dyn Error>>
{
let channel = get_channel(&self.api_endpoint).await?;
Ok(AuthServiceClient::with_interceptor(
channel,
self.get_chained_interceptor(),
))
}

pub async fn build_management_client(
&self,
) -> Result<
ManagementServiceClient<InterceptedService<Channel, ChainedInterceptor>>,
Box<dyn Error>,
> {
let channel = get_channel(&self.api_endpoint).await?;
Ok(ManagementServiceClient::with_interceptor(
channel,
self.get_chained_interceptor(),
))
}

fn get_chained_interceptor(&self) -> ChainedInterceptor {
let mut interceptor = ChainedInterceptor::new();
match &self.auth_type {
AuthType::AccessToken(token) => {
interceptor =
interceptor.add_interceptor(Box::new(AccessTokenInterceptor::new(token)));
}
AuthType::ServiceAccount(service_account, auth_options) => {
interceptor =
interceptor.add_interceptor(Box::new(ServiceAccountInterceptor::new(
&self.api_endpoint,
service_account,
auth_options.clone(),
)));
}
_ => {}
}

// return if let Some(access_token) = &self.access_token {
// Ok(AdminServiceClient::with_interceptor(
// channel,
// Box::new(move |mut req: Request<()>| Ok(req)),
// ))
// } else if let Some(service_account) = &self.service_account {
// Ok(AdminServiceClient::with_interceptor(
// channel,
// ServiceAccountInterceptor::new(
// &self.api_endpoint,
// service_account,
// self.auth_options.clone(),
// ),
// ))
// } else {
// Ok(AdminServiceClient::with_interceptor(
// channel,
// NoopInterceptor {},
// ))
// };
interceptor
}
}

Expand Down
26 changes: 21 additions & 5 deletions src/api/interceptors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,29 @@ use tonic::{service::Interceptor, Request, Status};

use crate::credentials::{AuthenticationOptions, ServiceAccount};

/// A simple noop interceptor that implements the Interceptor trait.
/// This allows the client builder to always return an intercepted
/// client.
pub(crate) struct NoopInterceptor;
pub struct ChainedInterceptor {
interceptors: Vec<Box<dyn Interceptor>>,
}

impl ChainedInterceptor {
pub fn new() -> Self {
Self {
interceptors: Vec::new(),
}
}

impl Interceptor for NoopInterceptor {
pub fn add_interceptor(mut self, interceptor: Box<dyn Interceptor>) -> Self {
self.interceptors.push(interceptor);
self
}
}

impl Interceptor for ChainedInterceptor {
fn call(&mut self, request: Request<()>) -> Result<Request<()>, Status> {
let mut request = request;
for interceptor in &mut self.interceptors {
request = interceptor.call(request)?;
}
Ok(request)
}
}
Expand Down

0 comments on commit 473c465

Please sign in to comment.