1
+ use std:: collections:: HashSet ;
2
+
1
3
use crate :: controllers;
2
4
use crate :: controllers:: util:: RequestPartsExt ;
5
+ use crate :: middleware:: app:: RequestApp ;
3
6
use crate :: middleware:: log_request:: RequestLogExt ;
4
7
use crate :: middleware:: session:: RequestSession ;
5
8
use crate :: models:: token:: { CrateScope , EndpointScope } ;
@@ -16,6 +19,7 @@ pub struct AuthCheck {
16
19
allow_token : bool ,
17
20
endpoint_scope : Option < EndpointScope > ,
18
21
crate_name : Option < String > ,
22
+ require_admin : bool ,
19
23
}
20
24
21
25
impl AuthCheck {
@@ -27,6 +31,7 @@ impl AuthCheck {
27
31
allow_token : true ,
28
32
endpoint_scope : None ,
29
33
crate_name : None ,
34
+ require_admin : false ,
30
35
}
31
36
}
32
37
@@ -36,6 +41,7 @@ impl AuthCheck {
36
41
allow_token : false ,
37
42
endpoint_scope : None ,
38
43
crate_name : None ,
44
+ require_admin : false ,
39
45
}
40
46
}
41
47
@@ -44,6 +50,7 @@ impl AuthCheck {
44
50
allow_token : self . allow_token ,
45
51
endpoint_scope : Some ( endpoint_scope) ,
46
52
crate_name : self . crate_name . clone ( ) ,
53
+ require_admin : self . require_admin ,
47
54
}
48
55
}
49
56
@@ -52,6 +59,16 @@ impl AuthCheck {
52
59
allow_token : self . allow_token ,
53
60
endpoint_scope : self . endpoint_scope ,
54
61
crate_name : Some ( crate_name. to_string ( ) ) ,
62
+ require_admin : self . require_admin ,
63
+ }
64
+ }
65
+
66
+ pub fn require_admin ( & self ) -> Self {
67
+ Self {
68
+ allow_token : self . allow_token ,
69
+ endpoint_scope : self . endpoint_scope ,
70
+ crate_name : self . crate_name . clone ( ) ,
71
+ require_admin : true ,
55
72
}
56
73
}
57
74
@@ -62,10 +79,14 @@ impl AuthCheck {
62
79
conn : & mut PgConnection ,
63
80
) -> AppResult < Authentication > {
64
81
let auth = authenticate ( request, conn) ?;
65
- self . check_authentication ( auth)
82
+ self . check_authentication ( auth, & request . app ( ) . config . admin_user_github_ids )
66
83
}
67
84
68
- pub fn check_authentication ( & self , auth : Authentication ) -> AppResult < Authentication > {
85
+ fn check_authentication (
86
+ & self ,
87
+ auth : Authentication ,
88
+ gh_admin_user_ids : & HashSet < i32 > ,
89
+ ) -> AppResult < Authentication > {
69
90
if let Some ( token) = auth. api_token ( ) {
70
91
if !self . allow_token {
71
92
let error_message =
@@ -84,6 +105,11 @@ impl AuthCheck {
84
105
}
85
106
}
86
107
108
+ if self . require_admin && !gh_admin_user_ids. contains ( & auth. user ( ) . gh_id ) {
109
+ let error_message = "User is unauthorized" ;
110
+ return Err ( internal ( error_message) . chain ( forbidden ( ) ) ) ;
111
+ }
112
+
87
113
Ok ( auth)
88
114
}
89
115
@@ -350,4 +376,41 @@ mod tests {
350
376
assert ! ( !auth_check. crate_scope_matches( Some ( & vec![ cs( "anyhow" ) ] ) ) ) ;
351
377
assert ! ( !auth_check. crate_scope_matches( Some ( & vec![ cs( "actix-*" ) ] ) ) ) ;
352
378
}
379
+
380
+ #[ test]
381
+ fn require_admin ( ) {
382
+ let auth_check = AuthCheck :: default ( ) . require_admin ( ) ;
383
+ let gh_admin_user_ids = [ 42 , 43 ] . into_iter ( ) . collect ( ) ;
384
+
385
+ assert_ok ! ( auth_check. check_authentication( mock_cookie( 42 ) , & gh_admin_user_ids) ) ;
386
+ assert_err ! ( auth_check. check_authentication( mock_cookie( 44 ) , & gh_admin_user_ids) ) ;
387
+ assert_ok ! ( auth_check. check_authentication( mock_token( 43 ) , & gh_admin_user_ids) ) ;
388
+ assert_err ! ( auth_check. check_authentication( mock_token( 45 ) , & gh_admin_user_ids) ) ;
389
+ }
390
+
391
+ fn mock_user ( gh_id : i32 ) -> User {
392
+ User {
393
+ id : 3 ,
394
+ gh_access_token : "arbitrary" . into ( ) ,
395
+ gh_login : "literally_anything" . into ( ) ,
396
+ name : None ,
397
+ gh_avatar : None ,
398
+ gh_id,
399
+ account_lock_reason : None ,
400
+ account_lock_until : None ,
401
+ }
402
+ }
403
+
404
+ fn mock_cookie ( gh_id : i32 ) -> Authentication {
405
+ Authentication :: Cookie ( CookieAuthentication {
406
+ user : mock_user ( gh_id) ,
407
+ } )
408
+ }
409
+
410
+ fn mock_token ( gh_id : i32 ) -> Authentication {
411
+ Authentication :: Token ( TokenAuthentication {
412
+ token : ApiToken :: mock_builder ( ) . user_id ( gh_id) . build ( ) . unwrap ( ) ,
413
+ user : mock_user ( gh_id) ,
414
+ } )
415
+ }
353
416
}
0 commit comments