-
Notifications
You must be signed in to change notification settings - Fork 55
Expand file tree
/
Copy pathdot_ssh.rs
More file actions
129 lines (108 loc) · 3.84 KB
/
dot_ssh.rs
File metadata and controls
129 lines (108 loc) · 3.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! `~/.ssh` support.
use crate::{Fingerprint, PrivateKey, PublicKey, Result};
use std::{
env,
fs::{self, ReadDir},
path::{Path, PathBuf},
};
/// `~/.ssh` directory support (or similarly structured directories).
#[derive(Clone, Eq, PartialEq)]
pub struct DotSsh {
path: PathBuf,
}
impl DotSsh {
/// Open `~/.ssh` if the home directory can be located.
///
/// Returns `None` if the home directory couldn't be located.
pub fn new() -> Option<Self> {
#[allow(deprecated)] // NOTE: no longer deprecated as of Rust 1.86
env::home_dir().map(|path| Self::open(path.join(".ssh")))
}
/// Open a `~/.ssh`-structured directory.
///
/// Does not verify that the directory exists or has the right file permissions.
///
/// Attempts to canonicalize the path once opened.
pub fn open(path: impl Into<PathBuf>) -> Self {
let path = path.into();
Self {
path: path.canonicalize().unwrap_or(path),
}
}
/// Get the path to the `~/.ssh` directory (or whatever [`DotSsh::open`] was called with).
pub fn path(&self) -> &Path {
&self.path
}
/// Get the path to the `~/.ssh/config` configuration file. Does not check if it exists.
pub fn config_path(&self) -> PathBuf {
self.path.join("config")
}
/// Iterate over the private keys in the `~/.ssh` directory.
pub fn private_keys(&self) -> Result<impl Iterator<Item = PrivateKey>> {
Ok(PrivateKeysIter {
read_dir: fs::read_dir(&self.path)?,
})
}
/// Find a private key whose public key has the given key fingerprint.
pub fn private_key_with_fingerprint(&self, fingerprint: Fingerprint) -> Option<PrivateKey> {
self.private_keys()
.ok()?
.find(|key| key.public_key().fingerprint(fingerprint.algorithm()) == fingerprint)
}
/// Iterate over the public keys in the `~/.ssh` directory.
pub fn public_keys(&self) -> Result<impl Iterator<Item = PublicKey>> {
Ok(PublicKeysIter {
read_dir: fs::read_dir(&self.path)?,
})
}
/// Find a public key with the given key fingerprint.
pub fn public_key_with_fingerprint(&self, fingerprint: Fingerprint) -> Option<PublicKey> {
self.public_keys()
.ok()?
.find(|key| key.fingerprint(fingerprint.algorithm()) == fingerprint)
}
/// Write a private key into `~/.ssh`.
pub fn write_private_key(&self, filename: impl AsRef<Path>, key: &PrivateKey) -> Result<()> {
key.write_openssh_file(self.path.join(filename), Default::default())
}
/// Write a public key into `~/.ssh`.
pub fn write_public_key(&self, filename: impl AsRef<Path>, key: &PublicKey) -> Result<()> {
key.write_openssh_file(self.path.join(filename))
}
}
impl Default for DotSsh {
/// Calls [`DotSsh::new`] and panics if the home directory could not be located.
fn default() -> Self {
Self::new().expect("home directory could not be located")
}
}
/// Iterator over the private keys in the `~/.ssh` directory.
pub struct PrivateKeysIter {
read_dir: ReadDir,
}
impl Iterator for PrivateKeysIter {
type Item = PrivateKey;
fn next(&mut self) -> Option<Self::Item> {
loop {
let entry = self.read_dir.next()?.ok()?;
if let Ok(key) = PrivateKey::read_openssh_file(entry.path()) {
return Some(key);
}
}
}
}
/// Iterator over the public keys in the `~/.ssh` directory.
pub struct PublicKeysIter {
read_dir: ReadDir,
}
impl Iterator for PublicKeysIter {
type Item = PublicKey;
fn next(&mut self) -> Option<Self::Item> {
loop {
let entry = self.read_dir.next()?.ok()?;
if let Ok(key) = PublicKey::read_openssh_file(entry.path()) {
return Some(key);
}
}
}
}