atsh_lib/ssh/
remote.rs

1use prettytable::{Cell, Row, Table};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::io::{BufRead, BufReader};
4use std::io::{Error, ErrorKind};
5use std::process::{Command, Stdio};
6use tracing::{debug, info, warn};
7
8use super::secure::{check_secure, decrypt, encrypt};
9use super::session::SSHSession;
10use crate::config::CONFIG;
11use crate::db::{self, delete_index, get_connection, update_authorized};
12
13#[derive(Debug, Default, Serialize, Deserialize, Clone)]
14pub struct Remote {
15    /// the index of the remote server.
16    // #[serde(rename = "idx")]
17    pub index: usize,
18    /// the login user.
19    pub user: String,
20    /// the login password.
21    #[serde(deserialize_with = "depass", serialize_with = "enpass")]
22    pub password: String,
23    /// the login id address.
24    pub ip: String,
25    /// the login port.
26    pub port: u16,
27    /// have the authorized to login.
28    pub authorized: bool,
29    /// the alias name for the login.
30    pub name: Option<String>,
31    /// the note for the server.
32    pub note: Option<String>,
33}
34
35fn depass<'de, D>(deserializer: D) -> Result<String, D::Error>
36where
37    D: Deserializer<'de>,
38{
39    let password = String::deserialize(deserializer)?;
40    Ok(decrypt(&password))
41}
42
43fn enpass<S>(password: &String, serializer: S) -> Result<S::Ok, S::Error>
44where
45    S: Serializer,
46{
47    Serialize::serialize(&encrypt(password), serializer)
48}
49
50// impl display for Remote
51impl std::fmt::Display for Remote {
52    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
53        write!(f, "{}@{}:{}", self.user, self.ip, self.port,)
54    }
55}
56
57impl Remote {
58    // 确保先从数据库查询
59    pub fn delete(&self) -> Result<(), Error> {
60        // 删除认证
61        let session = SSHSession::new(&self.user, &self.password, &self.ip, self.port)?;
62        session.revoke()?;
63        // 删除数据库
64        let conn = get_connection().lock();
65        delete_index(&conn, self.index).map_err(|e| Error::new(std::io::ErrorKind::Other, e))?;
66        info!(remote = self.to_string(), "success delete");
67        Ok(())
68    }
69
70    fn authenticate(&self) -> Result<(), Error> {
71        // 认证
72        let session = SSHSession::new(&self.user, &self.password, &self.ip, self.port)?;
73        session.authenticate()?;
74        // 更新数据库
75        if !self.authorized {
76            // update authorized to database
77            // self.authorized = true;
78            let conn = get_connection().lock();
79            update_authorized(&conn, self.index, true)
80                .map_err(|e| Error::new(std::io::ErrorKind::Other, e))?;
81        }
82        info!(remote = self.to_string(), "success authenticate");
83        Ok(())
84    }
85
86    pub fn login(&self, reauth: bool) -> Result<(), Error> {
87        // 如果没有认证,或者通过 `--auth` 参数重新认证
88        if !self.authorized || reauth {
89            self.authenticate()?;
90        }
91
92        let sshkey = CONFIG.get_private_key();
93        Command::new("ssh")
94            .arg(format!("{}@{}", self.user, self.ip))
95            .arg("-p")
96            .arg(self.port.to_string())
97            .arg("-i")
98            .arg(sshkey.to_str().unwrap())
99            .status()?;
100        info!(remote = self.to_string(), "success login");
101        Ok(())
102    }
103
104    fn scp(&self, args: &Vec<&str>) -> Result<(), Error> {
105        // 如果没有认证,则先认证
106        if !self.authorized {
107            debug!(remote = self.to_string(), "no authorized, try authenticate");
108            self.authenticate()?;
109        }
110        // info!("\n🚨 scp {}\n🚨 input `y` to run and other to cancel.", cmd);
111        // let mut read = String::new();
112        // std::io::stdin().read_line(&mut read)?;
113        // let read = read.trim();
114        // if read == "y" {}
115        debug!(args=?args, "scp");
116        let stderr = Command::new("scp")
117            .args(args)
118            .stderr(Stdio::piped())
119            .spawn()?
120            .stderr;
121        if stderr.is_none() {
122            return Err(Error::new(ErrorKind::BrokenPipe, "stderr is none"));
123        }
124        let reader = BufReader::new(stderr.unwrap());
125        reader
126            .lines()
127            .filter_map(|line| line.ok())
128            .for_each(|line| println!("{}", line));
129        Ok(())
130    }
131
132    pub fn upload(&self, from: &str, to: &str) -> Result<(), Error> {
133        // debug!(from = ?from, to=?to, "upload");
134        // scp -r -P 22 -i /home/idhyt/.ssh/id_rsa ./test.txt idhyt@1.2.3.4:/tmp
135        let port = self.port.to_string();
136        let remote = format!("{}@{}:{}", self.user, self.ip, to);
137        let cmd = vec![
138            "-r",
139            "-P",
140            &port,
141            "-i",
142            CONFIG.get_private_key().to_str().unwrap(),
143            from,
144            &remote,
145        ];
146        self.scp(&cmd)?;
147        info!(from=?from, to=?to, "susccess upload");
148        Ok(())
149    }
150
151    pub fn download(&self, from: &str, to: &str) -> Result<(), Error> {
152        // debug!(from = ?from, to=?to, "download");
153        // scp -r -P 22 -i /home/idhyt/.ssh/id_rsa idhyt@1.2.3.4:/tmp/test.txt ./
154        let port = self.port.to_string();
155        let remote = format!("{}@{}:{}", self.user, self.ip, from);
156        let cmd = vec![
157            "-r",
158            "-P",
159            &port,
160            "-i",
161            CONFIG.get_private_key().to_str().unwrap(),
162            &remote,
163            to,
164        ];
165        self.scp(&cmd)?;
166        info!(from=?from, to=?to, "susccess download");
167        Ok(())
168    }
169}
170
171#[derive(Debug, Default, Serialize, Deserialize, Clone)]
172pub struct Remotes(pub Vec<Remote>);
173
174impl Remotes {
175    fn load() -> Result<Remotes, Error> {
176        let conn = db::get_connection().lock();
177        let remotes = db::query_all(&conn).map_err(|e| Error::new(std::io::ErrorKind::Other, e))?;
178        Ok(Remotes(remotes))
179    }
180    pub fn get(idx: usize) -> Result<Option<Remote>, Error> {
181        let remote = {
182            let conn = db::get_connection().lock();
183            db::query_index(&conn, idx)
184        }
185        .map_err(|e| Error::new(std::io::ErrorKind::Other, e))?;
186
187        if remote.is_some() {
188            info!(index = idx, "susccess get remote");
189        } else {
190            warn!(index = idx, "remote not found");
191        }
192        Ok(remote)
193    }
194
195    pub fn try_get(idx: usize) -> Result<Remote, Error> {
196        let remote = Remotes::get(idx)?;
197        if remote.is_none() {
198            return Err(Error::new(
199                std::io::ErrorKind::NotFound,
200                format!("index {} remote not found", idx),
201            ));
202        }
203        Ok(remote.unwrap())
204    }
205
206    pub fn get_all() -> Result<Remotes, Error> {
207        let remotes = Remotes::load()?;
208        info!("susccess get all remotes {}", remotes.0.len());
209        Ok(remotes)
210    }
211
212    pub fn add(
213        user: &str,
214        password: &str,
215        ip: &str,
216        port: u16,
217        name: &Option<impl AsRef<str>>,
218        note: &Option<impl AsRef<str>>,
219    ) -> Result<usize, Error> {
220        check_secure()?;
221        let remote = Remote {
222            index: 0, // not used
223            user: user.to_string(),
224            password: password.to_string(),
225            ip: ip.to_string(),
226            port,
227            authorized: false,
228            name: name.as_ref().map(|n| n.as_ref().to_string()),
229            note: note.as_ref().map(|n| n.as_ref().to_string()),
230        };
231        // we not authorized the remote server until the first login
232        // remote.authorized();
233        // debug!(remote = remote.to_string(), "add");
234        let n = {
235            let conn = db::get_connection().lock();
236            db::insert(&conn, &remote)
237        }
238        .map_err(|e| Error::new(std::io::ErrorKind::Other, e))?;
239        info!(remote = remote.to_string(), "add remote success");
240        Ok(n)
241    }
242
243    pub fn delete(index: &Vec<usize>) -> Result<usize, Error> {
244        let mut n = 0;
245
246        for idx in index.iter() {
247            debug!(index = idx, "delete");
248            if let Some(remote) = Remotes::get(*idx)? {
249                remote.delete()?;
250                n += 1;
251            }
252        }
253        Ok(n)
254    }
255
256    // pub fn list() -> Result<(), Error> {
257    //     Remotes::get_all()?.pprint(false);
258    //     Ok(())
259    // }
260
261    // pub fn list_all() -> Result<(), Error> {
262    //     Remotes::get_all()?.pprint(true);
263    //     Ok(())
264    // }
265
266    pub fn pprint(&self, all: bool) {
267        let mut table = Table::new();
268        let mut titles = vec!["index", "name", "user", "ip", "port"];
269        if all {
270            titles.push("password");
271            titles.push("authorized");
272            titles.push("note");
273        }
274        table.set_titles(Row::new(
275            titles
276                .iter()
277                .map(|v| Cell::new(v).style_spec("bcFg"))
278                .collect::<Vec<Cell>>(),
279        ));
280
281        for remote in self.0.iter() {
282            let mut row = vec![
283                remote.index.to_string(),
284                remote.name.clone().unwrap_or_else(|| "".to_string()),
285                remote.user.clone(),
286                remote.ip.clone(),
287                remote.port.to_string(),
288            ];
289            if all {
290                if let Ok(_) = check_secure() {
291                    row.push(remote.password.clone());
292                } else {
293                    row.push(format!(
294                        "{}..{}",
295                        &remote.password[..3],
296                        &remote.password[remote.password.len() - 5..]
297                    ));
298                }
299                // row.push(remote.password.clone());
300                row.push(remote.authorized.to_string());
301                row.push(remote.note.clone().unwrap_or_else(|| "".to_string()));
302            }
303            table.add_row(Row::new(
304                row.iter()
305                    .map(|v| Cell::new(v).style_spec("lFc"))
306                    .collect::<Vec<Cell>>(),
307            ));
308        }
309        debug!("the remote list total: {}", self.0.len());
310        table.printstd();
311    }
312}