2023-08-19 15:42:29 +02:00
|
|
|
use async_trait::async_trait;
|
|
|
|
use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl};
|
2023-08-19 16:15:17 +02:00
|
|
|
use std::ops::Deref;
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
#[derive(Clone, clap::Args)]
|
|
|
|
pub struct OAuthClientClap {
|
|
|
|
#[clap(flatten)]
|
|
|
|
zitadel: Option<ZitadelConfig>,
|
|
|
|
|
|
|
|
#[clap(flatten)]
|
|
|
|
noop: Option<NoopConfig>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, clap::Args)]
|
|
|
|
#[group(conflicts_with = "ZitadelConfig")]
|
|
|
|
pub struct NoopConfig {
|
|
|
|
#[clap(env = "OAUTH_NOOP", long = "oauth-noop")]
|
|
|
|
pub oauth_noop: Option<bool>,
|
|
|
|
}
|
2023-08-19 15:42:29 +02:00
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
pub trait OAuthClient {
|
|
|
|
async fn get_token(&self) -> anyhow::Result<()>;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct OAuth(Arc<dyn OAuthClient + Send + Sync + 'static>);
|
|
|
|
|
|
|
|
impl OAuth {
|
|
|
|
pub fn new_zitadel(config: ZitadelConfig) -> Self {
|
|
|
|
Self(Arc::new(ZitadelOAuthClient::from(config)))
|
|
|
|
}
|
|
|
|
pub fn new_noop() -> Self {
|
|
|
|
Self(Arc::new(NoopOAuthClient {}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-19 16:15:17 +02:00
|
|
|
#[derive(Clone)]
|
2023-08-19 15:42:29 +02:00
|
|
|
pub enum OAuthConfig {
|
|
|
|
Zitadel(ZitadelConfig),
|
|
|
|
Noop,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<OAuthConfig> for OAuth {
|
|
|
|
fn from(value: OAuthConfig) -> Self {
|
|
|
|
match value {
|
|
|
|
OAuthConfig::Zitadel(c) => c.into(),
|
|
|
|
OAuthConfig::Noop => Self::new_noop(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Deref for OAuth {
|
|
|
|
type Target = Arc<dyn OAuthClient + Send + Sync + 'static>;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ZitadelConfig> for OAuth {
|
|
|
|
fn from(value: ZitadelConfig) -> Self {
|
|
|
|
Self::new_zitadel(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -- Noop
|
2023-08-19 16:15:17 +02:00
|
|
|
#[derive(clap::Args, Clone)]
|
2023-08-19 15:42:29 +02:00
|
|
|
pub struct NoopOAuthClient;
|
|
|
|
#[async_trait]
|
|
|
|
impl OAuthClient for NoopOAuthClient {
|
|
|
|
async fn get_token(&self) -> anyhow::Result<()> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -- Zitadel
|
|
|
|
|
2023-08-19 16:15:17 +02:00
|
|
|
#[derive(clap::Args, Clone)]
|
|
|
|
#[group(conflicts_with = "NoopConfig")]
|
2023-08-19 15:42:29 +02:00
|
|
|
pub struct ZitadelConfig {
|
2023-08-19 16:15:17 +02:00
|
|
|
#[clap(env = "ZITADEL_AUTH_URL", long = "zitadel-auth-url")]
|
|
|
|
auth_url: String,
|
|
|
|
#[clap(env = "ZITADEL_CLIENT_ID", long = "zitadel-client-id")]
|
2023-08-19 15:42:29 +02:00
|
|
|
client_id: String,
|
2023-08-19 16:15:17 +02:00
|
|
|
#[clap(env = "ZITADEL_CLIENT_SECRET", long = "zitadel-client-secret")]
|
2023-08-19 15:42:29 +02:00
|
|
|
client_secret: String,
|
2023-08-19 16:15:17 +02:00
|
|
|
#[clap(env = "ZITADEL_REDIRECT_URL", long = "zitadel-redirect-url")]
|
2023-08-19 15:42:29 +02:00
|
|
|
redirect_url: String,
|
2023-08-19 16:15:17 +02:00
|
|
|
#[clap(env = "ZITADEL_TOKEN_URL", long = "zitadel-token-url")]
|
2023-08-19 15:42:29 +02:00
|
|
|
token_url: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ZitadelOAuthClient {
|
|
|
|
client: BasicClient,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ZitadelOAuthClient {
|
|
|
|
pub fn new(
|
|
|
|
client_id: impl Into<String>,
|
|
|
|
client_secret: impl Into<String>,
|
|
|
|
redirect_url: impl Into<String>,
|
|
|
|
auth_url: impl Into<String>,
|
|
|
|
token_url: impl Into<String>,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
client: Self::oauth_client(ZitadelConfig {
|
|
|
|
client_id: client_id.into(),
|
|
|
|
client_secret: client_secret.into(),
|
|
|
|
redirect_url: redirect_url.into(),
|
|
|
|
auth_url: auth_url.into(),
|
|
|
|
token_url: token_url.into(),
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn oauth_client(config: ZitadelConfig) -> BasicClient {
|
|
|
|
BasicClient::new(
|
|
|
|
ClientId::new(config.client_id),
|
|
|
|
Some(ClientSecret::new(config.client_secret)),
|
|
|
|
AuthUrl::new(config.auth_url).unwrap(),
|
|
|
|
Some(TokenUrl::new(config.token_url).unwrap()),
|
|
|
|
)
|
|
|
|
.set_redirect_uri(RedirectUrl::new(config.redirect_url).unwrap())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ZitadelConfig> for ZitadelOAuthClient {
|
|
|
|
fn from(value: ZitadelConfig) -> Self {
|
|
|
|
Self::new(
|
|
|
|
value.client_id,
|
|
|
|
value.client_secret,
|
|
|
|
value.redirect_url,
|
|
|
|
value.auth_url,
|
|
|
|
value.token_url,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
impl OAuthClient for ZitadelOAuthClient {
|
|
|
|
async fn get_token(&self) -> anyhow::Result<()> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-08-19 16:15:17 +02:00
|
|
|
use crate::oauth::{OAuth, OAuthClientClap, OAuthConfig, ZitadelConfig};
|
|
|
|
use clap::Parser;
|
|
|
|
|
|
|
|
#[derive(Parser)]
|
|
|
|
#[command()]
|
|
|
|
pub struct Cli {
|
|
|
|
#[clap(flatten)]
|
|
|
|
options: OAuthClientClap,
|
|
|
|
}
|
2023-08-19 15:42:29 +02:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_noop() {
|
|
|
|
OAuth::from(OAuthConfig::Noop).get_token().await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_zitadel() {
|
|
|
|
OAuth::from(OAuthConfig::Zitadel(ZitadelConfig {
|
|
|
|
client_id: "something".into(),
|
|
|
|
client_secret: "something".into(),
|
|
|
|
redirect_url: "https://something".into(),
|
|
|
|
auth_url: "https://something".into(),
|
|
|
|
token_url: "https://something".into(),
|
|
|
|
}))
|
|
|
|
.get_token()
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
}
|
2023-08-19 16:15:17 +02:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_parse_clap() {
|
|
|
|
let cmd = Cli::parse_from(&["--oauth-noop"]);
|
|
|
|
}
|
2023-08-19 15:42:29 +02:00
|
|
|
}
|