1
1
use crate :: { cli, fs:: ConsoleWarningEmitter , http:: HttpClient } ;
2
+ use camino:: Utf8Path ;
2
3
use gleam_core:: {
3
4
Error , Result , Warning , encryption, hex, paths:: global_hexpm_credentials_path,
4
5
warning:: WarningEmitter ,
5
6
} ;
7
+ use serde:: { Deserialize , Serialize } ;
6
8
use std:: { rc:: Rc , time:: SystemTime } ;
7
9
8
10
pub const USER_PROMPT : & str = "https://hex.pm username" ;
@@ -12,15 +14,17 @@ pub const LOCAL_PASS_PROMPT: &str = "Local password";
12
14
pub const PASS_ENV_NAME : & str = "HEXPM_PASS" ;
13
15
pub const API_ENV_NAME : & str = "HEXPM_API_KEY" ;
14
16
15
- #[ derive( Debug ) ]
17
+ #[ derive( Debug , Serialize , Deserialize ) ]
16
18
pub struct EncryptedApiKey {
17
19
pub name : String ,
18
20
pub encrypted : String ,
21
+ pub username : Option < String > ,
19
22
}
20
23
21
24
#[ derive( Debug ) ]
22
25
pub struct UnencryptedApiKey {
23
26
pub unencrypted : String ,
27
+ pub username : Option < String > ,
24
28
}
25
29
26
30
pub struct HexAuthentication < ' runtime > {
@@ -74,11 +78,18 @@ encrypt your Hex API key.
74
78
detail : e. to_string ( ) ,
75
79
} ) ?;
76
80
77
- crate :: fs:: write ( & path, & format ! ( "{name}\n {encrypted}" ) ) ?;
81
+ let encrypted = EncryptedApiKey {
82
+ name,
83
+ encrypted,
84
+ username : Some ( username. clone ( ) ) ,
85
+ } ;
86
+
87
+ encrypted. save ( & path) ?;
78
88
println ! ( "Encrypted Hex API key written to {path}" ) ;
79
89
80
90
Ok ( UnencryptedApiKey {
81
91
unencrypted : api_key,
92
+ username : Some ( username) ,
82
93
} )
83
94
}
84
95
@@ -117,7 +128,12 @@ encrypt your Hex API key.
117
128
}
118
129
119
130
fn read_and_decrypt_stored_api_key ( & mut self ) -> Result < Option < UnencryptedApiKey > > {
120
- let Some ( EncryptedApiKey { encrypted, .. } ) = self . read_stored_api_key ( ) ? else {
131
+ let Some ( EncryptedApiKey {
132
+ encrypted,
133
+ username,
134
+ ..
135
+ } ) = self . read_stored_api_key ( ) ?
136
+ else {
121
137
return Ok ( None ) ;
122
138
} ;
123
139
@@ -127,26 +143,43 @@ encrypt your Hex API key.
127
143
detail : e. to_string ( ) ,
128
144
} ) ?;
129
145
130
- Ok ( Some ( UnencryptedApiKey { unencrypted } ) )
146
+ Ok ( Some ( UnencryptedApiKey {
147
+ unencrypted,
148
+ username,
149
+ } ) )
131
150
}
132
151
133
152
pub fn read_stored_api_key ( & self ) -> Result < Option < EncryptedApiKey > > {
134
153
let path = global_hexpm_credentials_path ( ) ;
135
154
if !path. exists ( ) {
136
155
return Ok ( None ) ;
137
156
}
138
- let text = crate :: fs:: read ( & path) ?;
139
- let mut chunks = text. splitn ( 2 , '\n' ) ;
140
- let Some ( name) = chunks. next ( ) else {
141
- return Ok ( None ) ;
142
- } ;
143
- let Some ( encrypted) = chunks. next ( ) else {
144
- return Ok ( None ) ;
145
- } ;
146
- Ok ( Some ( EncryptedApiKey {
147
- name : name. to_string ( ) ,
148
- encrypted : encrypted. to_string ( ) ,
149
- } ) )
157
+
158
+ let encrypted_key = EncryptedApiKey :: load ( & path) ;
159
+
160
+ if let Ok ( encrypted_key) = encrypted_key {
161
+ Ok ( Some ( encrypted_key) )
162
+ } else {
163
+ // fallback from old format
164
+ let text = crate :: fs:: read ( & path) ?;
165
+ let mut chunks = text. splitn ( 2 , '\n' ) ;
166
+ let Some ( name) = chunks. next ( ) else {
167
+ return Ok ( None ) ;
168
+ } ;
169
+ let Some ( encrypted) = chunks. next ( ) else {
170
+ return Ok ( None ) ;
171
+ } ;
172
+
173
+ let key = EncryptedApiKey {
174
+ name : name. to_string ( ) ,
175
+ encrypted : encrypted. to_string ( ) ,
176
+ username : None ,
177
+ } ;
178
+
179
+ key. save ( & path) ?;
180
+
181
+ Ok ( Some ( key) )
182
+ }
150
183
}
151
184
}
152
185
@@ -158,6 +191,25 @@ impl Drop for HexAuthentication<'_> {
158
191
}
159
192
}
160
193
194
+ impl EncryptedApiKey {
195
+ pub fn save ( & self , path : & Utf8Path ) -> Result < ( ) > {
196
+ let text = toml:: to_string ( self ) . map_err ( |_| Error :: InvalidCredentialsFile {
197
+ path : path. to_string ( ) ,
198
+ } ) ?;
199
+ crate :: fs:: write ( path, & text) ?;
200
+ Ok ( ( ) )
201
+ }
202
+
203
+ pub fn load ( path : & Utf8Path ) -> Result < Self > {
204
+ let text = crate :: fs:: read ( path) ?;
205
+ let key = toml:: from_str ( & text) . map_err ( |_| Error :: InvalidCredentialsFile {
206
+ path : path. to_string ( ) ,
207
+ } ) ?;
208
+
209
+ Ok ( key)
210
+ }
211
+ }
212
+
161
213
fn ask_local_password ( warnings : & mut Vec < Warning > ) -> std:: result:: Result < String , Error > {
162
214
std:: env:: var ( PASS_ENV_NAME )
163
215
. inspect ( |_| {
0 commit comments