commit 933cde3ff2aee1e275d1e777aad2ccf221a6b553
parent 441b37187fed12cadc53f3579d156083e94a8215
Author: Henrique Chan Carvalho Machado <henriqueccmachado@tecnico.ulisboa.pt>
Date: Mon, 19 Jan 2026 19:25:11 +0100
Update kych-client-management to use unified config
The kych-client-management CLI is aligned with the unified configuration by
requiring a global -c/--config flag and reading the database URL and client
definitions from it. The sync command no longer takes a config argument, manual
INI parsing has been removed, and client synchronization now uses pre-parsed
ClientConfig entries from Config::from_file().
Diffstat:
1 file changed, 41 insertions(+), 69 deletions(-)
diff --git a/kych_oauth2_gateway/src/bin/client_management_cli.rs b/kych_oauth2_gateway/src/bin/client_management_cli.rs
@@ -2,29 +2,28 @@
//!
//! Command-line tool for managing OAuth2 Gateway clients.
//!
-//! Set DATABASE_URL environment variable to connect to the database.
//! Usage:
-//! kych-client-management list
-//! kych-client-management show <client_id>
-//! kych-client-management create --client-id <id> --secret <secret> ...
-//! kych-client-management update <client_id> --webhook-url <url>
-//! kych-client-management sync kych.conf
-//! kych-client-management delete <client_id>
+//! kych-client-management --config kych.conf list
+//! kych-client-management --config kych.conf show <client_id>
+//! kych-client-management --config kych.conf create --client-id <id> --secret <secret> ...
+//! kych-client-management --config kych.conf update <client_id> --webhook-url <url>
+//! kych-client-management --config kych.conf sync
+//! kych-client-management --config kych.conf delete <client_id>
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
-use ini::Ini;
-use kych_oauth2_gateway_lib::db;
-use std::env;
+use kych_oauth2_gateway_lib::{config::Config, db};
use std::collections::HashSet;
#[derive(Parser, Debug)]
#[command(name = "client-mgmt")]
-
#[command(version)]
#[command(about = "OAuth2 Gateway client management CLI")]
-#[command(after_help = "Environment variables:\n DATABASE_URL PostgreSQL connection string (required)")]
struct Args {
+ /// Path to kych.conf configuration file
+ #[arg(long, short = 'c', required = true)]
+ config: String,
+
#[command(subcommand)]
command: Commands,
}
@@ -88,9 +87,6 @@ enum Commands {
/// Sync clients from configuration file (reads [client_*] sections)
Sync {
- /// Path to kych.conf file
- config_file: String,
-
/// Remove clients not in config file
#[arg(long)]
prune: bool,
@@ -107,21 +103,18 @@ enum Commands {
#[tokio::main]
async fn main() -> Result<()> {
- // Load .env (ignore if missing)
- let _ = dotenvy::dotenv();
-
let args = Args::parse();
- let database_url = env::var("DATABASE_URL")
- .context("DATABASE_URL environment variable not set")?;
+ let config = Config::from_file(&args.config)
+ .context(format!("Failed to load config from '{}'", args.config))?;
- let pool = db::create_pool(&database_url)
+ let pool = db::create_pool(&config.database.url)
.await
.context("Failed to connect to database")?;
match args.command {
Commands::List => cmd_list_clients(&pool).await?,
- Commands::Show { client_id } => cmd_show_client(&pool,&client_id).await?,
+ Commands::Show { client_id } => cmd_show_client(&pool, &client_id).await?,
Commands::Create {
client_id,
secret,
@@ -162,8 +155,8 @@ async fn main() -> Result<()> {
)
.await?
}
- Commands::Sync { config_file, prune } => {
- cmd_sync_clients(&pool, &config_file, prune).await?
+ Commands::Sync { prune } => {
+ cmd_sync_clients(&pool, &config, prune).await?
}
Commands::Delete { client_id, yes } => {
cmd_delete_client(&pool, &client_id, yes).await?
@@ -326,74 +319,53 @@ async fn cmd_delete_client(pool: &sqlx::PgPool, client_id: &str, skip_confirm: b
Ok(())
}
-async fn cmd_sync_clients(pool: &sqlx::PgPool, config_file: &str, prune: bool) -> Result<()> {
- println!("Loading clients from: {}", config_file);
-
- let ini = Ini::load_from_file(config_file)
- .context("Failed to load configuration file")?;
+async fn cmd_sync_clients(pool: &sqlx::PgPool, config: &Config, prune: bool) -> Result<()> {
+ println!("Syncing {} client(s) from configuration...", config.clients.len());
let mut synced_client_ids = HashSet::new();
let mut created_count = 0;
let mut updated_count = 0;
- for (section_name, properties) in ini.iter() {
- let section_name = match section_name {
- Some(name) if name.starts_with("client_") => name,
- _ => continue,
- };
-
- println!("\nProcessing section: [{}]", section_name);
-
- let client_id = properties.get("CLIENT_ID")
- .ok_or_else(|| anyhow::anyhow!("Missing CLIENT_ID in section [{}]", section_name))?;
- let client_secret = properties.get("CLIENT_SECRET")
- .ok_or_else(|| anyhow::anyhow!("Missing CLIENT_SECRET in section [{}]", section_name))?;
- let webhook_url = properties.get("WEBHOOK_URL")
- .ok_or_else(|| anyhow::anyhow!("Missing WEBHOOK_URL in section [{}]", section_name))?;
- let verifier_url = properties.get("VERIFIER_URL")
- .ok_or_else(|| anyhow::anyhow!("Missing VERIFIER_URL in section [{}]", section_name))?;
-
- let verifier_api_path = properties.get("VERIFIER_MANAGEMENT_API_PATH");
- let redirect_uri = properties.get("REDIRECT_URI");
- let accepted_issuer_dids = properties.get("ACCEPTED_ISSUER_DIDS");
+ for client_config in &config.clients {
+ println!("\nProcessing section: [{}]", client_config.section_name);
- synced_client_ids.insert(client_id.to_string());
+ synced_client_ids.insert(client_config.client_id.clone());
- let existing_client = db::clients::get_client_by_id(pool, client_id).await?;
+ let existing_client = db::clients::get_client_by_id(pool, &client_config.client_id).await?;
match existing_client {
Some(existing) => {
- println!(" Client '{}' already exists, updating...", client_id);
+ println!(" Client '{}' already exists, updating...", client_config.client_id);
db::clients::update_client(
pool,
existing.id,
- Some(webhook_url),
- Some(verifier_url),
- verifier_api_path,
- redirect_uri,
- accepted_issuer_dids,
+ Some(&client_config.webhook_url),
+ Some(&client_config.verifier_url),
+ Some(&client_config.verifier_management_api_path),
+ client_config.redirect_uri.as_deref(),
+ client_config.accepted_issuer_dids.as_deref(),
)
.await
- .context(format!("Failed to update client '{}'", client_id))?;
+ .context(format!("Failed to update client '{}'", client_config.client_id))?;
updated_count += 1;
- println!(" Updated client '{}'", client_id);
+ println!(" Updated client '{}'", client_config.client_id);
}
None => {
- println!(" Creating new client '{}'...", client_id);
+ println!(" Creating new client '{}'...", client_config.client_id);
db::clients::register_client(
pool,
- client_id,
- client_secret,
- webhook_url,
- verifier_url,
- verifier_api_path,
- redirect_uri,
- accepted_issuer_dids,
+ &client_config.client_id,
+ &client_config.client_secret,
+ &client_config.webhook_url,
+ &client_config.verifier_url,
+ Some(&client_config.verifier_management_api_path),
+ client_config.redirect_uri.as_deref(),
+ client_config.accepted_issuer_dids.as_deref(),
)
.await
- .context(format!("Failed to create client '{}'", client_id))?;
+ .context(format!("Failed to create client '{}'", client_config.client_id))?;
created_count += 1;
- println!(" Created client '{}'", client_id);
+ println!(" Created client '{}'", client_config.client_id);
}
}
}