1pub struct CliProgressBar {
3 }
5
6impl CliProgressBar {
7 pub fn new(msg: &str) -> Self {
8 println!("[ProgressBar] Starting: {}", msg);
10 Self {}
11 }
12 pub fn set_progress(&self, percent: u64) {
13 println!("[ProgressBar] Progress: {}%", percent);
15 }
16 pub fn finish(&self) {
17 println!("[ProgressBar] Finished");
19 }
20}
21
22pub fn format_cli_output(msg: &str) -> String {
23 format!("\x1b[1;34m[auth-framework]\x1b[0m {}", msg)
25}
26#[cfg(feature = "cli")]
27use crate::AppConfig;
28#[cfg(feature = "cli")]
29use crate::migrations::MigrationCli;
30#[cfg(feature = "cli")]
31use clap::{Parser, Subcommand};
32#[cfg(feature = "cli")]
33use rpassword;
34#[cfg(feature = "cli")]
35use std::process;
36
37#[cfg(feature = "cli")]
38#[derive(Parser)]
39#[command(name = "auth-framework")]
40#[command(about = "Auth Framework CLI - Manage authentication and authorization")]
41pub struct Cli {
42 #[command(subcommand)]
43 pub command: Option<Commands>,
44
45 #[arg(short, long, default_value = "auth.toml")]
46 pub config: String,
47
48 #[arg(long)]
49 pub verbose: bool,
50
51 #[arg(short, long)]
52 pub dry_run: bool,
53}
54
55#[cfg(feature = "cli")]
56#[derive(Subcommand)]
57pub enum Commands {
58 Db {
60 #[command(subcommand)]
61 command: DbCommands,
62 },
63 User {
65 #[command(subcommand)]
66 command: UserCommands,
67 },
68 Role {
70 #[command(subcommand)]
71 command: RoleCommands,
72 },
73 System {
75 #[command(subcommand)]
76 command: SystemCommands,
77 },
78 Security {
80 #[command(subcommand)]
81 command: SecurityCommands,
82 },
83}
84
85#[cfg(feature = "cli")]
86#[derive(Subcommand)]
87pub enum DbCommands {
88 Migrate,
90 Status,
92 Reset {
94 #[arg(long)]
95 confirm: bool,
96 },
97 CreateMigration { name: String },
99}
100
101#[cfg(feature = "cli")]
102#[derive(Subcommand)]
103pub enum UserCommands {
104 List {
106 #[arg(short, long)]
107 limit: Option<usize>,
108 #[arg(short, long)]
109 offset: Option<usize>,
110 #[arg(long)]
111 active_only: bool,
112 },
113 Create {
115 email: String,
116 #[arg(short, long)]
117 username: Option<String>,
118 #[arg(short, long)]
119 password: Option<String>,
120 #[arg(long)]
121 admin: bool,
122 },
123 Update {
125 user_id: String,
126 #[arg(short, long)]
127 email: Option<String>,
128 #[arg(short, long)]
129 active: Option<bool>,
130 },
131 Delete {
133 user_id: String,
134 #[arg(long)]
135 confirm: bool,
136 },
137 ResetPassword {
139 user_id: String,
140 #[arg(short, long)]
141 password: Option<String>,
142 },
143 Show { user_id: String },
145}
146
147#[cfg(feature = "cli")]
148#[derive(Subcommand)]
149pub enum RoleCommands {
150 List,
152 Create {
154 name: String,
155 #[arg(short, long)]
156 description: Option<String>,
157 },
158 Assign { user_id: String, role_name: String },
160 Remove { user_id: String, role_name: String },
162 Permissions { role_name: String },
164 AddPermission {
166 role_name: String,
167 permission: String,
168 },
169}
170
171#[cfg(feature = "cli")]
172#[derive(Subcommand)]
173pub enum SystemCommands {
174 Status,
176 Health,
178 Config {
180 #[arg(short, long)]
181 output: Option<String>,
182 },
183 Backup { output_path: String },
185 Restore {
187 backup_path: String,
188 #[arg(long)]
189 confirm: bool,
190 },
191}
192
193#[cfg(feature = "cli")]
194#[derive(Subcommand)]
195pub enum SecurityCommands {
196 Audit {
198 #[arg(short, long)]
199 days: Option<u32>,
200 },
201 Sessions {
203 #[arg(short, long)]
204 user_id: Option<String>,
205 },
206 TerminateSession {
208 session_id: String,
209 #[arg(long)]
210 reason: Option<String>,
211 },
212 LockUser {
214 user_id: String,
215 #[arg(short, long)]
216 reason: Option<String>,
217 },
218 UnlockUser { user_id: String },
220}
221
222#[cfg(feature = "cli")]
223pub struct CliHandler {
224 config: AppConfig,
225 }
227
228#[cfg(feature = "cli")]
229impl CliHandler {
230 pub async fn new(config: AppConfig) -> Result<Self, Box<dyn std::error::Error>> {
231 Ok(Self { config })
233 }
234
235 pub async fn handle_command(&mut self, cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
236 match cli.command {
237 Some(Commands::Db { command }) => self.handle_db_command(command).await?,
238 Some(Commands::User { command }) => self.handle_user_command(command).await?,
239 Some(Commands::Role { command }) => self.handle_role_command(command).await?,
240 Some(Commands::System { command }) => self.handle_system_command(command).await?,
241 Some(Commands::Security { command }) => self.handle_security_command(command).await?,
242 None => {
243 eprintln!("No command provided. Use --help for usage.");
244 }
245 }
246 Ok(())
247 }
248
249 async fn handle_db_command(
250 &mut self,
251 command: DbCommands,
252 ) -> Result<(), Box<dyn std::error::Error>> {
253 match command {
254 DbCommands::Migrate => {
255 println!("Running database migrations...");
256 MigrationCli::run(&self.config.database.url, "migrate").await?;
257 }
258 DbCommands::Status => {
259 MigrationCli::run(&self.config.database.url, "status").await?;
260 }
261 DbCommands::Reset { confirm } => {
262 if !confirm {
263 eprintln!("ERROR: Database reset requires --confirm flag");
264 eprintln!("WARNING: This will destroy all data!");
265 process::exit(1);
266 }
267 println!("Resetting database...");
268 }
270 DbCommands::CreateMigration { name } => {
271 println!("Creating migration: {}", name);
272 }
274 }
275 Ok(())
276 }
277
278 async fn handle_user_command(
279 &mut self,
280 command: UserCommands,
281 ) -> Result<(), Box<dyn std::error::Error>> {
282 match command {
283 UserCommands::List {
284 limit: _limit,
285 offset: _offset,
286 active_only: _active_only,
287 } => {
288 println!("Listing users...");
289 }
291 UserCommands::Create {
292 email,
293 username: _username,
294 password: _password,
295 admin: _admin,
296 } => {
297 println!("Creating user: {}", email);
298
299 let _password = if let Some(pwd) = _password {
300 pwd
301 } else {
302 rpassword::prompt_password("Password: ")?
304 };
305
306 println!("User created successfully");
308 }
309 UserCommands::Show { user_id } => {
310 println!("User details for: {}", user_id);
311 }
313 UserCommands::Update {
314 user_id,
315 email,
316 active,
317 } => {
318 println!("Updating user: {}", user_id);
319 if let Some(email) = email {
320 println!(" New email: {}", email);
321 }
323 if let Some(active) = active {
324 println!(" Setting active status to: {}", active);
325 }
327 println!("User updated successfully");
328 }
329 UserCommands::Delete { user_id, confirm } => {
330 if !confirm {
331 eprintln!("ERROR: User deletion requires --confirm flag");
332 eprintln!("WARNING: This will permanently delete the user!");
333 process::exit(1);
334 }
335 println!("Deleting user: {}", user_id);
336 println!("User deleted successfully");
338 }
339 UserCommands::ResetPassword { user_id, password } => {
340 println!("Resetting password for user: {}", user_id);
341 let _new_password = if let Some(pwd) = password {
342 pwd
343 } else {
344 rpassword::prompt_password("New password: ")?
345 };
346 println!("Password reset successfully");
348 }
349 }
350 Ok(())
351 }
352
353 async fn handle_role_command(
354 &mut self,
355 command: RoleCommands,
356 ) -> Result<(), Box<dyn std::error::Error>> {
357 match command {
358 RoleCommands::List => {
359 println!("Available roles:");
360 }
362 RoleCommands::Create {
363 name,
364 description: _,
365 } => {
366 println!("Creating role: {}", name);
367 }
369 RoleCommands::Assign { user_id, role_name } => {
370 println!("Assigning role '{}' to user {}", role_name, user_id);
371 }
373 RoleCommands::Remove { user_id, role_name } => {
374 println!("Removing role '{}' from user {}", role_name, user_id);
375 println!("Role removed successfully");
377 }
378 RoleCommands::Permissions { role_name } => {
379 println!("Permissions for role '{}':", role_name);
380 println!(" • read:users");
382 println!(" • write:users");
383 }
385 RoleCommands::AddPermission {
386 role_name,
387 permission,
388 } => {
389 println!("Adding permission '{}' to role '{}'", permission, role_name);
390 println!("Permission added successfully");
392 }
393 }
394 Ok(())
395 }
396
397 async fn handle_system_command(
398 &mut self,
399 command: SystemCommands,
400 ) -> Result<(), Box<dyn std::error::Error>> {
401 match command {
402 SystemCommands::Status => {
403 println!("System Status:");
404 println!(" Database: Connected");
405 println!(
406 " Redis: {}",
407 if self.config.redis.is_some() {
408 "Connected"
409 } else {
410 "Not configured"
411 }
412 );
413 }
415 SystemCommands::Health => {
416 println!("Running health checks...");
418
419 println!("✓ Database connectivity");
421
422 if self.config.redis.is_some() {
424 println!("✓ Redis connectivity");
425 }
426
427 println!("✓ Database migrations");
429
430 println!("All health checks passed");
431 }
432 SystemCommands::Config { output } => {
433 let template = include_str!("../config/auth.toml.template");
434 if let Some(path) = output {
435 std::fs::write(&path, template)?;
436 println!("Configuration template written to: {}", path);
437 } else {
438 println!("{}", template);
439 }
440 }
441 SystemCommands::Backup { output_path } => {
442 println!("Creating backup at: {}", output_path);
443 println!("Backup completed successfully");
448 }
449 SystemCommands::Restore {
450 backup_path,
451 confirm,
452 } => {
453 if !confirm {
454 eprintln!("ERROR: Database restore requires --confirm flag");
455 eprintln!("WARNING: This will overwrite existing data!");
456 process::exit(1);
457 }
458 println!("Restoring from backup: {}", backup_path);
459 println!("Restore completed successfully");
465 }
466 }
467 Ok(())
468 }
469
470 async fn handle_security_command(
471 &mut self,
472 command: SecurityCommands,
473 ) -> Result<(), Box<dyn std::error::Error>> {
474 match command {
475 SecurityCommands::Audit { days } => {
476 let days = days.unwrap_or(7);
477 println!("Security audit for last {} days:", days);
478 }
480 SecurityCommands::Sessions { user_id } => {
481 if let Some(user_id) = user_id {
482 println!("Active sessions for user: {}", user_id);
483 } else {
484 println!("All active sessions:");
485 }
486 }
488 SecurityCommands::LockUser { user_id, reason: _ } => {
489 println!("Locking user account: {}", user_id);
490 }
492 SecurityCommands::TerminateSession { session_id, reason } => {
493 println!("Terminating session: {}", session_id);
494 if let Some(reason) = reason {
495 println!(" Reason: {}", reason);
496 }
497 println!("Session terminated successfully");
499 }
500 SecurityCommands::UnlockUser { user_id } => {
501 println!("Unlocking user account: {}", user_id);
502 println!("User account unlocked successfully");
504 }
505 }
506 Ok(())
507 }
508}
509
510#[cfg(feature = "cli")]
511pub async fn run_cli() -> Result<(), Box<dyn std::error::Error>> {
513 let cli = Cli::parse();
514
515 let config = AppConfig::from_env()?;
517
518 let mut handler = CliHandler::new(config).await?;
520 handler.handle_command(cli).await?;
521
522 Ok(())
523}
524
525#[cfg(test)]
527mod tests {
528 use super::{CliProgressBar, format_cli_output};
529 #[test]
530 fn test_progress_bar() {
531 let pb = CliProgressBar::new("Test");
532 pb.set_progress(50);
533 pb.finish();
534 }
535 #[test]
536 fn test_terminal_formatting() {
537 let msg = format_cli_output("Hello");
538 assert!(msg.contains("[auth-framework]"));
539 }
540}
541
542