1
- use std:: sync:: Arc ;
1
+ use std:: { path :: PathBuf , sync:: Arc } ;
2
2
3
- use bindle:: client:: {
4
- tokens:: { HttpBasic , NoToken , TokenManager } ,
5
- Client , ClientBuilder ,
3
+ use anyhow:: Result ;
4
+ use bindle:: {
5
+ client:: {
6
+ tokens:: { HttpBasic , NoToken , TokenManager } ,
7
+ Client , ClientBuilder ,
8
+ } ,
9
+ invoice:: signature:: { KeyEntry , KeyRing } ,
6
10
} ;
11
+ use tracing:: log;
7
12
8
13
/// BindleConnectionInfo holds the details of a connection to a
9
14
/// Bindle server, including url, insecure configuration and an
@@ -13,38 +18,83 @@ pub struct BindleConnectionInfo {
13
18
base_url : String ,
14
19
allow_insecure : bool ,
15
20
token_manager : AnyAuth ,
21
+ keyring_path : PathBuf ,
16
22
}
17
23
18
24
impl BindleConnectionInfo {
19
25
/// Generates a new BindleConnectionInfo instance using the provided
20
26
/// base_url, allow_insecure setting and optional username and password
21
27
/// for basic http auth
22
- pub fn new < I : Into < String > > (
28
+ pub async fn new < I : Into < String > > (
23
29
base_url : I ,
24
30
allow_insecure : bool ,
25
31
username : Option < String > ,
26
32
password : Option < String > ,
27
- ) -> Self {
33
+ keyring_file : Option < PathBuf > ,
34
+ ) -> Result < Self > {
28
35
let token_manager: Box < dyn TokenManager + Send + Sync > = match ( username, password) {
29
36
( Some ( u) , Some ( p) ) => Box :: new ( HttpBasic :: new ( & u, & p) ) ,
30
37
_ => Box :: new ( NoToken :: default ( ) ) ,
31
38
} ;
32
39
33
- Self {
40
+ let keyring_path = match keyring_file {
41
+ Some ( dir) => dir,
42
+ None => {
43
+ let dir = ensure_config_dir ( ) . await ?;
44
+ dir. join ( "keyring.toml" )
45
+ }
46
+ } ;
47
+
48
+ Ok ( Self {
34
49
base_url : base_url. into ( ) ,
35
50
allow_insecure,
36
51
token_manager : AnyAuth {
37
52
token_manager : Arc :: new ( token_manager) ,
38
53
} ,
39
- }
54
+ keyring_path,
55
+ } )
40
56
}
41
57
42
58
/// Returns a client based on this instance's configuration
43
- pub fn client ( & self ) -> bindle:: client:: Result < Client < AnyAuth > > {
44
- let builder = ClientBuilder :: default ( )
59
+ pub async fn client ( & self ) -> bindle:: client:: Result < Client < AnyAuth > > {
60
+ let mut keyring = read_bindle_keyring ( & self . keyring_path )
61
+ . await
62
+ . unwrap_or_else ( |e| {
63
+ log:: error!(
64
+ "can't read bindle keyring file {:?}, err: {:?}" ,
65
+ & self . keyring_path,
66
+ e
67
+ ) ;
68
+ KeyRing :: default ( )
69
+ } ) ;
70
+
71
+ let tmp_client = ClientBuilder :: default ( )
72
+ . http2_prior_knowledge ( false )
73
+ . danger_accept_invalid_certs ( self . allow_insecure )
74
+ . build (
75
+ & self . base_url ,
76
+ self . token_manager . clone ( ) ,
77
+ Arc :: new ( keyring. clone ( ) ) ,
78
+ ) ?;
79
+
80
+ log:: trace!( "Fetching host keys from bindle server" ) ;
81
+ let host_keys = tmp_client. get_host_keys ( ) . await ?;
82
+ let filtered_keys: Vec < KeyEntry > = host_keys
83
+ . key
84
+ . into_iter ( )
85
+ . filter ( |k| !keyring. key . iter ( ) . any ( |current| current. key == k. key ) )
86
+ . collect ( ) ;
87
+ keyring. key . extend ( filtered_keys) ;
88
+ log:: info!( "keyring: {:?}" , & keyring) ;
89
+
90
+ ClientBuilder :: default ( )
45
91
. http2_prior_knowledge ( false )
46
- . danger_accept_invalid_certs ( self . allow_insecure ) ;
47
- builder. build ( & self . base_url , self . token_manager . clone ( ) )
92
+ . danger_accept_invalid_certs ( self . allow_insecure )
93
+ . build (
94
+ & self . base_url ,
95
+ self . token_manager . clone ( ) ,
96
+ Arc :: new ( keyring) ,
97
+ )
48
98
}
49
99
}
50
100
@@ -64,3 +114,17 @@ impl TokenManager for AnyAuth {
64
114
self . token_manager . apply_auth_header ( builder) . await
65
115
}
66
116
}
117
+
118
+ async fn read_bindle_keyring ( keyring_path : & PathBuf ) -> bindle:: client:: Result < KeyRing > {
119
+ let raw_data = tokio:: fs:: read ( keyring_path) . await ?;
120
+ let res: KeyRing = toml:: from_slice ( & raw_data) ?;
121
+ Ok ( res)
122
+ }
123
+
124
+ async fn ensure_config_dir ( ) -> Result < PathBuf > {
125
+ let dir = dirs:: config_dir ( )
126
+ . map ( |v| v. join ( "bindle/" ) )
127
+ . unwrap_or_else ( || "./bindle" . into ( ) ) ;
128
+ tokio:: fs:: create_dir_all ( & dir) . await ?;
129
+ Ok ( dir)
130
+ }
0 commit comments