feat: with clap example

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2023-10-22 22:33:47 +02:00
parent 32d8030f24
commit 89acf8c343
Signed by: kjuulh
GPG Key ID: 9AA7BC13CE474394
7 changed files with 162 additions and 32 deletions

16
Cargo.lock generated
View File

@ -520,7 +520,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"axum", "axum",
"clap", "clap 4.4.6",
"nefarious-login", "nefarious-login",
"tokio", "tokio",
"tracing-subscriber", "tracing-subscriber",
@ -660,6 +660,18 @@ dependencies = [
"inout", "inout",
] ]
[[package]]
name = "clap"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"clap 4.4.6",
"nefarious-login",
"tokio",
"tracing-subscriber",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.6" version = "4.4.6"
@ -1876,7 +1888,7 @@ dependencies = [
"axum", "axum",
"axum-extra 0.7.7", "axum-extra 0.7.7",
"axum-sessions", "axum-sessions",
"clap", "clap 4.4.6",
"oauth2", "oauth2",
"openidconnect", "openidconnect",
"pretty_assertions", "pretty_assertions",

View File

@ -88,7 +88,7 @@ impl Auth for ZitadelAuthService {
Ok(( Ok((
headers, headers,
Url::parse("http://localhost:3000/dash/home") Url::parse("http://localhost:3001/authed")
.context("failed to parse login_authorized zitadel return url")?, .context("failed to parse login_authorized zitadel return url")?,
)) ))
} }

View File

@ -50,11 +50,8 @@ pub mod config {
} }
#[derive(clap::Args, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(clap::Args, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[group(requires_all = ["auth_url", "client_id", "client_secret", "redirect_url", "token_url", "authority_url"])] #[group(requires_all = ["client_id", "client_secret", "redirect_url", "authority_url"])]
pub struct ZitadelClap { pub struct ZitadelClap {
#[arg(env = "ZITADEL_AUTH_URL", long = "zitadel-auth-url")]
pub auth_url: Option<String>,
#[arg(env = "ZITADEL_CLIENT_ID", long = "zitadel-client-id")] #[arg(env = "ZITADEL_CLIENT_ID", long = "zitadel-client-id")]
pub client_id: Option<String>, pub client_id: Option<String>,
@ -66,9 +63,6 @@ pub mod config {
#[arg(env = "ZITADEL_AUTHORITY_URL", long = "zitadel-authority-url")] #[arg(env = "ZITADEL_AUTHORITY_URL", long = "zitadel-authority-url")]
pub authority_url: Option<String>, pub authority_url: Option<String>,
#[arg(env = "ZITADEL_TOKEN_URL", long = "zitadel-token-url")]
pub token_url: Option<String>,
} }
impl TryFrom<AuthClap> for OAuth { impl TryFrom<AuthClap> for OAuth {
@ -85,11 +79,6 @@ pub mod config {
impl AuthClap { impl AuthClap {
pub fn merge(&mut self, config: AuthConfigFile) -> &mut Self { pub fn merge(&mut self, config: AuthConfigFile) -> &mut Self {
if let Some(zitadel) = config.zitadel { if let Some(zitadel) = config.zitadel {
if let Some(auth_url) = zitadel.auth_url {
if self.zitadel.auth_url.is_some() {
_ = self.zitadel.auth_url.replace(auth_url);
}
}
if let Some(client_id) = zitadel.client_id { if let Some(client_id) = zitadel.client_id {
if self.zitadel.client_id.is_some() { if self.zitadel.client_id.is_some() {
_ = self.zitadel.client_id.replace(client_id); _ = self.zitadel.client_id.replace(client_id);
@ -110,11 +99,6 @@ pub mod config {
_ = self.zitadel.authority_url.replace(authority_url); _ = self.zitadel.authority_url.replace(authority_url);
} }
} }
if let Some(token_url) = zitadel.token_url {
if self.zitadel.token_url.is_some() {
_ = self.zitadel.token_url.replace(token_url);
}
}
} }
self self

View File

@ -78,10 +78,14 @@ impl TryFrom<ZitadelClap> for ZitadelConfig {
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_from(value: ZitadelClap) -> Result<Self, Self::Error> { fn try_from(value: ZitadelClap) -> Result<Self, Self::Error> {
let authority_url = value
.authority_url
.ok_or(anyhow::anyhow!("authority_url was not set"))?;
Ok(Self { Ok(Self {
auth_url: value auth_url: format!("{}/oauth/v2/authorize", &authority_url),
.auth_url token_url: format!("{}/oauth/v2/token", &authority_url),
.ok_or(anyhow::anyhow!("auth_url was not set"))?, authority_url,
client_id: value client_id: value
.client_id .client_id
.ok_or(anyhow::anyhow!("client_id was not set"))?, .ok_or(anyhow::anyhow!("client_id was not set"))?,
@ -91,12 +95,6 @@ impl TryFrom<ZitadelClap> for ZitadelConfig {
redirect_url: value redirect_url: value
.redirect_url .redirect_url
.ok_or(anyhow::anyhow!("redirect_url was not set"))?, .ok_or(anyhow::anyhow!("redirect_url was not set"))?,
token_url: value
.token_url
.ok_or(anyhow::anyhow!("token_url was not set"))?,
authority_url: value
.authority_url
.ok_or(anyhow::anyhow!("authority_url was not set"))?,
}) })
} }
} }

View File

@ -9,7 +9,15 @@ use axum::{
use nefarious_login::{ use nefarious_login::{
auth::AuthService, auth::AuthService,
axum::{AuthController, UserFromSession}, axum::{AuthController, UserFromSession},
introspection::IntrospectionService,
login::{
config::{AuthEngine, ZitadelClap},
AuthClap,
},
oauth::OAuth,
session::{PostgresqlSessionClap, SessionBackend, SessionService},
}; };
use tracing_subscriber::EnvFilter;
#[derive(Clone)] #[derive(Clone)]
struct AppState { struct AppState {
@ -18,10 +26,33 @@ struct AppState {
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt().init(); tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.init();
// Change to zitadel test instance let auth = AuthClap {
let auth_service = AuthService::new_noop(); engine: AuthEngine::Zitadel,
session_backend: SessionBackend::Postgresql,
zitadel: ZitadelClap {
authority_url: Some("https://personal-wxuujs.zitadel.cloud".into()),
client_id: Some("237412977047895154@nefarious-test".into()),
client_secret: Some(
"rWwDi8gjNOyuMFKoOjNSlhjcVZ1B25wDh6HsDL27f0g2Hb0xGbvEf0WXFY2akOlL".into(),
),
redirect_url: Some("http://localhost:3001/auth/authorized".into()),
},
session: nefarious_login::session::SessionClap {
postgresql: PostgresqlSessionClap {
conn: Some("postgres://como:somenotverysecurepassword@localhost:5432/como".into()),
},
},
};
let auth_service = AuthService::new_zitadel(
OAuth::try_from(auth.clone())?,
IntrospectionService::new_zitadel(&auth).await?,
SessionService::new(&auth).await?,
);
let state = AppState { let state = AppState {
auth: auth_service.clone(), auth: auth_service.clone(),
@ -35,6 +66,7 @@ async fn main() -> anyhow::Result<()> {
let addr = SocketAddr::from(([127, 0, 0, 1], 3001)); let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
println!("listening on: {addr}"); println!("listening on: {addr}");
println!("open browser at: http://localhost:3001/auth/zitadel");
axum::Server::bind(&addr) axum::Server::bind(&addr)
.serve(app.into_make_service()) .serve(app.into_make_service())
.await .await

16
examples/clap/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "clap"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nefarious-login.workspace = true
tokio.workspace = true
anyhow.workspace = true
axum.workspace = true
clap.workspace = true
tracing-subscriber.workspace = true

88
examples/clap/src/main.rs Normal file
View File

@ -0,0 +1,88 @@
use std::net::SocketAddr;
use axum::{
extract::{FromRef, State},
response::IntoResponse,
routing::get,
Router,
};
use clap::Parser;
use nefarious_login::{
auth::AuthService,
axum::{AuthController, UserFromSession},
login::AuthClap,
session::SessionService,
};
use tracing_subscriber::EnvFilter;
#[derive(Clone)]
struct AppState {
auth: AuthService,
}
#[derive(Debug, Clone, Parser)]
struct Command {
#[clap(flatten)]
auth: AuthClap,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.init();
let cmd = Command::parse_from(vec![
"base",
"--auth-engine=zitadel",
"--zitadel-authority-url=https://personal-wxuujs.zitadel.cloud",
"--zitadel-redirect-url=http://localhost:3001/auth/authorized",
"--zitadel-client-id=237412977047895154@nefarious-test",
"--zitadel-client-secret=rWwDi8gjNOyuMFKoOjNSlhjcVZ1B25wDh6HsDL27f0g2Hb0xGbvEf0WXFY2akOlL",
"--session-backend=postgresql",
"--session-postgres-conn=postgres://como:somenotverysecurepassword@localhost:5432/como",
]);
let auth = cmd.auth;
let session_service = SessionService::new(&auth).await?;
let auth_service = AuthService::new(&auth, session_service).await?;
let state = AppState {
auth: auth_service.clone(),
};
let app = Router::new()
.route("/unauthed", get(unauthed))
.route("/authed", get(authed))
.with_state(state)
.nest("/auth", AuthController::new_router(auth_service).await?);
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
println!("listening on: {addr}");
println!("open browser at: http://localhost:3001/auth/zitadel");
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
Ok(())
}
impl FromRef<AppState> for AuthService {
fn from_ref(input: &AppState) -> Self {
input.auth.clone()
}
}
async fn unauthed() -> String {
"Hello Unauthorized User".into()
}
#[axum::debug_handler()]
async fn authed(
user: UserFromSession,
State(_auth_service): State<AuthService>,
) -> impl IntoResponse {
format!("Hello authorized user: {:?}", user.user.id)
}