use clap::{Parser, Subcommand};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Parser, Debug)]
#[command(
name = "gen-auth-token",
about = "Generates 512-bit authentication tokens for the DB-Sync system",
version = "0.1.0",
author = "DB-Sync Team"
)]
struct Args {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
Generate {
#[arg(long)]
system_ip: String,
#[arg(long)]
system_name: Option<String>,
#[arg(long)]
secret_key: Option<String>,
#[arg(long, default_value = "text")]
format: String,
},
Batch {
#[arg(long)]
config_file: String,
#[arg(long, default_value = "text")]
format: String,
},
Validate {
#[arg(long)]
token: String,
},
Example,
}
fn generate_secure_token_512(system_ip: &str, system_name: &str, secret_key: &str) -> String {
let mut token = String::new();
for i in 0..8 {
let mut hasher = DefaultHasher::new();
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let combined = format!("{}:{}:{}:{}:{}", system_ip, system_name, secret_key, timestamp, i);
combined.hash(&mut hasher);
token.push_str(&format!("{:016x}", hasher.finish()));
}
token.truncate(128);
token
}
fn validate_token_format(token: &str) -> bool {
if token.len() != 128 {
return false;
}
token.chars().all(|c| c.is_ascii_hexdigit())
}
fn main() {
let args = Args::parse();
match args.command {
Commands::Generate { system_ip, system_name, secret_key, format } => {
let system_name = system_name.unwrap_or_else(|| "Unknown System".to_string());
let secret_key = secret_key.unwrap_or_else(|| "db_sync_default_secret_key".to_string());
let token = generate_secure_token_512(&system_ip, &system_name, &secret_key);
match format.as_str() {
"json" => {
println!("{}", serde_json::json!({
"system_ip": system_ip,
"system_name": system_name,
"token": token,
"token_length_bits": token.len() * 4,
"generated_at": SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
}));
}
"text" => {
println!("=== DB-Sync Authentication Token Generation ===");
println!("System IP: {}", system_ip);
println!("System Name: {}", system_name);
println!("Token Length: {} bits", token.len() * 4);
println!("Generated Token: {}", token);
println!("Generated At: {}", SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs());
}
_ => {
eprintln!("Error: Unsupported output format '{}'", format);
std::process::exit(1);
}
}
}
Commands::Batch { config_file, format } => {
let config_content = match std::fs::read_to_string(&config_file) {
Ok(content) => content,
Err(e) => {
eprintln!("错误:无法读取配置文件 '{}': {}", config_file, e);
std::process::exit(1);
}
};
let config: serde_json::Value = match serde_json::from_str(&config_content) {
Ok(config) => config,
Err(e) => {
eprintln!("错误:无法解析配置文件: {}", e);
std::process::exit(1);
}
};
let systems = config.get("systems").and_then(|v| v.as_array());
if systems.is_none() {
eprintln!("Error: 'systems' array missing in configuration file");
std::process::exit(1);
}
let default_secret = config.get("secret_key")
.and_then(|v| v.as_str())
.unwrap_or("db_sync_default_secret_key");
match format.as_str() {
"json" => {
let mut results = serde_json::Map::new();
for system in systems.unwrap() {
let system_ip = system.get("system_ip").and_then(|v| v.as_str());
let system_name = system.get("system_name").and_then(|v| v.as_str());
let secret_key = system.get("secret_key").and_then(|v| v.as_str()).unwrap_or(default_secret);
if let (Some(ip), Some(name)) = (system_ip, system_name) {
let token = generate_secure_token_512(ip, name, secret_key);
results.insert(ip.to_string(), serde_json::json!({
"system_name": name,
"token": token,
"token_length_bits": token.len() * 4
}));
}
}
println!("{}", serde_json::json!({
"generated_tokens": results,
"generated_at": SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
}));
}
"text" => {
println!("=== DB-Sync Batch Token Generation ===");
for system in systems.unwrap() {
let system_ip = system.get("system_ip").and_then(|v| v.as_str());
let system_name = system.get("system_name").and_then(|v| v.as_str());
let secret_key = system.get("secret_key").and_then(|v| v.as_str()).unwrap_or(default_secret);
if let (Some(ip), Some(name)) = (system_ip, system_name) {
let token = generate_secure_token_512(ip, name, secret_key);
println!("\nSystem: {}", name);
println!("IP: {}", ip);
println!("Token: {}", token);
println!("Length: {} bits", token.len() * 4);
}
}
}
_ => {
eprintln!("Error: Unsupported output format '{}'", format);
std::process::exit(1);
}
}
}
Commands::Validate { token } => {
let is_valid = validate_token_format(&token);
if is_valid {
println!("✓ Token format is valid");
println!(" Length: {} characters ({} bits)", token.len(), token.len() * 4);
println!(" Format: Hexadecimal string");
} else {
println!("✗ Token format is invalid");
if token.len() != 128 {
println!(" Length Error: Expected 128 characters, got {}", token.len());
}
if !token.chars().all(|c| c.is_ascii_hexdigit()) {
println!(" Character Error: Contains non-hexadecimal characters");
}
std::process::exit(1);
}
}
Commands::Example => {
let example_config = serde_json::json!({
"secret_key": "your_custom_secret_key_here",
"systems": [
{
"system_ip": "192.168.1.100",
"system_name": "HR System",
"secret_key": "optional_override_for_this_system"
},
{
"system_ip": "192.168.1.101",
"system_name": "Asset Management System"
},
{
"system_ip": "192.168.1.102",
"system_name": "Database Server"
}
]
});
println!("=== Example Configuration File (batch_config.json) ===");
println!("{}", serde_json::to_string_pretty(&example_config).unwrap());
println!("\n=== Usage Examples ===");
println!("# Generate a token for a single system");
println!("cargo run --bin gen-auth-token generate --system-ip 192.168.1.100 --system-name 'HR System'");
println!();
println!("# Generate tokens in batch");
println!("cargo run --bin gen-auth-token batch --config-file batch_config.json --format json");
println!();
println!("# Validate a token");
println!("cargo run --bin gen-auth-token validate --token 'your_token_here'");
}
}
}