1
1
use crate :: app:: AppState ;
2
- use crate :: email:: Email ;
2
+ use crate :: email:: { Email , EmailMessage } ;
3
3
use crate :: models:: { ApiToken , User } ;
4
4
use crate :: schema:: { api_tokens, crate_owners, crates, emails} ;
5
5
use crate :: util:: errors:: { AppResult , BoxedAppError , bad_request} ;
@@ -16,6 +16,7 @@ use diesel::prelude::*;
16
16
use diesel_async:: { AsyncPgConnection , RunQueryDsl } ;
17
17
use futures_util:: TryStreamExt ;
18
18
use http:: HeaderMap ;
19
+ use minijinja:: context;
19
20
use p256:: PublicKey ;
20
21
use p256:: ecdsa:: VerifyingKey ;
21
22
use p256:: ecdsa:: signature:: Verifier ;
@@ -25,6 +26,7 @@ use std::str::FromStr;
25
26
use std:: sync:: LazyLock ;
26
27
use std:: time:: Duration ;
27
28
use tokio:: sync:: Mutex ;
29
+ use tracing:: warn;
28
30
29
31
// Minimum number of seconds to wait before refreshing cache of GitHub's public keys
30
32
const PUBLIC_KEY_CACHE_LIFETIME : Duration = Duration :: from_secs ( 60 * 60 * 24 ) ; // 24 hours
@@ -226,13 +228,16 @@ async fn send_notification_email(
226
228
return Err ( anyhow ! ( "No address found" ) ) ;
227
229
} ;
228
230
229
- let email = TokenExposedEmail {
230
- domain : & state. config . domain_name ,
231
- reporter : "GitHub" ,
232
- source : & alert. source ,
233
- token_name : & token. name ,
234
- url : & alert. url ,
235
- } ;
231
+ let email = EmailMessage :: from_template (
232
+ "token_exposed" ,
233
+ context ! {
234
+ domain => state. config. domain_name,
235
+ reporter => "GitHub" ,
236
+ source => alert. source,
237
+ token_name => token. name,
238
+ url => if alert. url. is_empty( ) { "" } else { & alert. url }
239
+ } ,
240
+ ) ?;
236
241
237
242
state. emails . send ( & recipient, email) . await ?;
238
243
@@ -304,46 +309,6 @@ async fn send_trustpub_notification_emails(
304
309
Ok ( ( ) )
305
310
}
306
311
307
- struct TokenExposedEmail < ' a > {
308
- domain : & ' a str ,
309
- reporter : & ' a str ,
310
- source : & ' a str ,
311
- token_name : & ' a str ,
312
- url : & ' a str ,
313
- }
314
-
315
- impl Email for TokenExposedEmail < ' _ > {
316
- fn subject ( & self ) -> String {
317
- format ! (
318
- "crates.io: Your API token \" {}\" has been revoked" ,
319
- self . token_name
320
- )
321
- }
322
-
323
- fn body ( & self ) -> String {
324
- let mut body = format ! (
325
- "{reporter} has notified us that your crates.io API token {token_name} \
326
- has been exposed publicly. We have revoked this token as a precaution.
327
-
328
- Please review your account at https://{domain} to confirm that no \
329
- unexpected changes have been made to your settings or crates.
330
-
331
- Source type: {source}" ,
332
- domain = self . domain,
333
- reporter = self . reporter,
334
- source = self . source,
335
- token_name = self . token_name,
336
- ) ;
337
- if self . url . is_empty ( ) {
338
- body. push_str ( "\n \n We were not informed of the URL where the token was found." ) ;
339
- } else {
340
- body. push_str ( & format ! ( "\n \n URL where the token was found: {}" , self . url) ) ;
341
- }
342
-
343
- body
344
- }
345
- }
346
-
347
312
struct TrustedPublishingTokenExposedEmail < ' a > {
348
313
domain : & ' a str ,
349
314
reporter : & ' a str ,
0 commit comments