mysqladm/client/commands/
passwd_user.rs1use anyhow::Context;
2use clap::Parser;
3use dialoguer::Password;
4use futures_util::SinkExt;
5use tokio_stream::StreamExt;
6
7use crate::{
8 client::commands::erroneous_server_response,
9 core::{
10 protocol::{
11 ClientToServerMessageStream, ListUsersError, Request, Response,
12 print_set_password_output_status,
13 },
14 types::MySQLUser,
15 },
16};
17
18#[derive(Parser, Debug, Clone)]
19pub struct PasswdUserArgs {
20 username: MySQLUser,
21
22 #[clap(short, long)]
23 password_file: Option<String>,
24
25 #[arg(short, long)]
27 json: bool,
28}
29
30pub fn read_password_from_stdin_with_double_check(username: &MySQLUser) -> anyhow::Result<String> {
31 Password::new()
32 .with_prompt(format!("New MySQL password for user '{}'", username))
33 .with_confirmation(
34 format!("Retype new MySQL password for user '{}'", username),
35 "Passwords do not match",
36 )
37 .interact()
38 .map_err(Into::into)
39}
40
41pub async fn passwd_user(
42 args: PasswdUserArgs,
43 mut server_connection: ClientToServerMessageStream,
44) -> anyhow::Result<()> {
45 let message = Request::ListUsers(Some(vec![args.username.to_owned()]));
47 if let Err(err) = server_connection.send(message).await {
48 server_connection.close().await.ok();
49 anyhow::bail!(err);
50 }
51 let response = match server_connection.next().await {
52 Some(Ok(Response::ListUsers(users))) => users,
53 response => return erroneous_server_response(response),
54 };
55 match response
56 .get(&args.username)
57 .unwrap_or(&Err(ListUsersError::UserDoesNotExist))
58 {
59 Ok(_) => {}
60 Err(err) => {
61 server_connection.send(Request::Exit).await?;
62 server_connection.close().await.ok();
63 anyhow::bail!("{}", err.to_error_message(&args.username));
64 }
65 }
66
67 let password = if let Some(password_file) = args.password_file {
68 std::fs::read_to_string(password_file)
69 .context("Failed to read password file")?
70 .trim()
71 .to_string()
72 } else {
73 read_password_from_stdin_with_double_check(&args.username)?
74 };
75
76 let message = Request::PasswdUser((args.username.to_owned(), password));
77
78 if let Err(err) = server_connection.send(message).await {
79 server_connection.close().await.ok();
80 anyhow::bail!(err);
81 }
82
83 let result = match server_connection.next().await {
84 Some(Ok(Response::SetUserPassword(result))) => result,
85 response => return erroneous_server_response(response),
86 };
87
88 server_connection.send(Request::Exit).await?;
89
90 print_set_password_output_status(&result, &args.username);
91
92 Ok(())
93}